관리 메뉴

AI 세상 ?

[Django REST Framework] Tutorial 본문

Development/Django

[Django REST Framework] Tutorial

phpdoumi 2017. 3. 11. 18:40

원본 : http://www.django-rest-framework.org/tutorial/1-serialization/

Tutorial 1: Serialization

Introduction

튜토리얼에서는 API 강조하는 간단한 pastebin 코드 작성에 대해 다룹니다. 길을 따라 REST framework 구성하는 다양한 구성 요소를 소개하고 모든 것이 어떻게 맞는지에 대한 포괄적인 이해를 제공합니다.

 

튜토리얼은 상당히 심층적이므로 시작하기 전에 쿠키와 좋아하는 양조주 컵을 준비해야 합니다. 빠른 개요만 보려면 quickstart 설명서로 이동해야 합니다.


참고 : 튜토리얼의 코드는 GitHub tomchristie/rest-framework-tutorial 리포지토리에서 사용할 있습니다. 완료된 구현은 테스트를 위한 샌드 박스 버전으로 온라인에서도 사용할 있습니다.


Setting up a new environment

다른 일을 하기 전에 virtualenv 사용하여 새로운 가상 환경을 만듭니다. 이렇게 하면 우리 패키지 구성이 우리가 작업하고 있는 다른 프로젝트와 격리되도록 있습니다.

virtualenv env

source env/bin/activate

이제 우리는 virtualenv 환경에 있고 패키지 요구 사항을 설치할 있습니다.

pip install django

pip install djangorestframework

pip install pygments  # We'll be using this for the code highlighting

참고 : 언제든지 virtualenv 환경을 종료하려면 deactivate 입력. 자세한 내용은 virtualenv 설명서를 참조하십시오.

Getting started

좋아, 코딩 준비가 됐어. 시작하려면 프로젝트를 만들어 보겠습니다.

cd ~

django-admin.py startproject tutorial

cd tutorial

일단 끝나면 간단한 API 만드는 사용할 앱을 만들 있습니다.

python manage.py startapp snippets

INSTALLED_APPS 새로운 snippets 앱과 rest_framework 앱을 추가해야 합니다. tutorial/settings.py 파일을 수정해 보겠습니다.

INSTALLED_APPS = (

    ...

    'rest_framework',

    'snippets.apps.SnippetsConfig',

)

Django <1.9 사용하는 경우, snippets.apps.SnippetsConfig snippets으로 대체해야 합니다.

 

좋아, 이제 준비 할게.

Creating a model to work with

튜토리얼에서는 코드 스니펫을 저장하는 사용되는 간단한 Snippet 모델을 작성하는 것으로 시작합니다. snippets/models.py 파일을 수정하십시오. 참고: 좋은 프로그래밍 방법에는 주석이 포함됩니다. 튜토리얼 코드의 저장소 버전에서 찾을 있지만 여기서는 코드 자체에 중점을 두고 생략했습니다.

from django.db import models

from pygments.lexers import get_all_lexers

from pygments.styles import get_all_styles

 

LEXERS = [item for item in get_all_lexers() if item[1]]

LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])

STYLE_CHOICES = sorted((item, item) for item in get_all_styles())

 

 

class Snippet(models.Model):

    created = models.DateTimeField(auto_now_add=True)

    title = models.CharField(max_length=100, blank=True, default='')

    code = models.TextField()

    linenos = models.BooleanField(default=False)

    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)

    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

 

    class Meta:

        ordering = ('created',)

또한 스니펫 모델에 대한 초기 migration 작성하고 처음으로 데이터베이스를 동기화해야 합니다.

python manage.py makemigrations snippets

python manage.py migrate

Creating a Serializer class

API 시작하기 위해 가장 먼저해야 일은 snippet 인스턴스를 json 같은 표현으로 직렬화 직렬화하는 방법을 제공하는 것입니다. 우리는 Django 형식과 매우 유사한 serializer 선언하여 작업을 수행 있습니다. snippets 디렉토리에 serializers.py라는 파일을 만들고 serializers.py 추가하십시오.

from rest_framework import serializers

from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES

 

 

class SnippetSerializer(serializers.Serializer):

    id = serializers.IntegerField(read_only=True)

    title = serializers.CharField(required=False, allow_blank=True, max_length=100)

    code = serializers.CharField(style={'base_template': 'textarea.html'})

    linenos = serializers.BooleanField(required=False)

    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')

    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

 

    def create(self, validated_data):

        """

        Create and return a new `Snippet` instance, given the validated data.

        """

        return Snippet.objects.create(**validated_data)

 

    def update(self, instance, validated_data):

        """

        Update and return an existing `Snippet` instance, given the validated data.

        """

        instance.title = validated_data.get('title', instance.title)

        instance.code = validated_data.get('code', instance.code)

        instance.linenos = validated_data.get('linenos', instance.linenos)

        instance.language = validated_data.get('language', instance.language)

        instance.style = validated_data.get('style', instance.style)

        instance.save()

        return instance

serializer 클래스의 번째 부분은 serialize/deserialize되는 필드를 정의합니다. create () update () 메서드는 serializer.save () 호출할 본격적인 인스턴스가 생성되거나 수정되는 방법을 정의합니다.

 

serializer 클래스는 Django Form 클래스와 매우 유사하며 required, max_length default 같은 다양한 필드에 비슷한 유효성 검사 플래그를 포함합니다.

 

필드 플래그는 특정 상황(: HTML 렌더링할 )에서 serializer 표시하는 방법을 제어 수도 있습니다. 위의 { 'base_template': 'textarea.html'} 플래그는 Django Form 클래스에서 widget = widgets.Textarea 사용하는 것과 같습니다. 이는 특히 브라우저의 API 표시 방법을 제어하는 유용합니다(나중에 튜토리얼에서 있습니다).

 

우리는 나중에 보게 ModelSerializer 클래스를 사용하여 약간의 시간을 절약할 있습니다. 하지만 지금은 serializer 정의를 명시적으로 유지할 것입니다.

 

Working with Serializers

이상 배우기 전에 새로운 Serializer 클래스를 사용하는 것에 익숙해 것입니다. 장고 껍데기에 들어가 봅시다.

python manage.py shell

일단 우리가 import들을 가져 왔으면 가지 코드 스니펫을 만들어 보겠습니다.

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

from rest_framework.renderers import JSONRenderer

from rest_framework.parsers import JSONParser

 

snippet = Snippet(code='foo = "bar"\n')

snippet.save()

 

snippet = Snippet(code='print "hello, world"\n')

snippet.save()

우리는 이제 있는 스니펫 인스턴스를 얻었습니다. 이러한 인스턴스 하나를 serialize하는 방법을 살펴 보겠습니다.

serializer = SnippetSerializer(snippet)

serializer.data

# {'id': 2, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}

시점에서 모델 인스턴스를 파이썬 기본 데이터 유형으로 변환했습니다. 직렬화 과정을 마무리하기 위해 데이터를 json으로 렌더링합니다.

content = JSONRenderer().render(serializer.data)

content

# '{"id": 2, "title": "", "code": "print \\"hello, world\\"\\n", "linenos": false, "language": "python", "style": "friendly"}'

Deserialization( 직렬화) 간단합니다. 먼저 파이썬 원시 데이터 형식으로 스트림을 파싱합니다.

from django.utils.six import BytesIO

 

stream = BytesIO(content)

data = JSONParser().parse(stream)

... 그런 다음 원시 데이터 유형을 완전히 채워진 객체 인스턴스로 복원합니다.

serializer = SnippetSerializer(data=data)

serializer.is_valid()

# True

serializer.validated_data

# OrderedDict([('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])

serializer.save()

# <Snippet: Snippet object>

API 양식 작업과 얼마나 유사한 확인하십시오. 우리의 시리얼 라이저를 사용하는 뷰를 작성하기 시작할 유사성이 더욱 분명해진다.

 

모델 인스턴스 대신 쿼리 세트를 직렬화 수도 있습니다. 그렇게 하기 위해 단순히 serializer 인수에 many=True 플래그를 추가합니다.

serializer = SnippetSerializer(Snippet.objects.all(), many=True)

serializer.data

# [OrderedDict([('id', 1), ('title', u''), ('code', u'foo = "bar"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', u''), ('code', u'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', u''), ('code', u'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

Using ModelSerializers

Google SnippetSerializer 클래스는 Snippet 모델에도 포함된 많은 정보를 복제합니다. 코드를 간결하게 유지할 있다면 좋을 것입니다.

Django Form 클래스와 ModelForm 클래스를 제공하는 것과 같은 방식으로 REST 프레임 워크는 Serializer 클래스와 ModelSerializer 클래스를 모두 포함합니다.

ModelSerializer 클래스를 사용하여 serializer 리팩터링하는 방법을 살펴 보겠습니다. 파일 snippets/serializers.py 다시 열고 SnippetSerializer 클래스를 다음으로 바꿉니다.

class SnippetSerializer(serializers.ModelSerializer):

    class Meta:

        model = Snippet

        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

serializer 가지고 있는 좋은 속성 하나는 serializer 인스턴스의 모든 필드를 표현할 있다는 것입니다. Django shell python manage.py shell 열고 다음을 시도하십시오:

from snippets.serializers import SnippetSerializer

serializer = SnippetSerializer()

print(repr(serializer))

# SnippetSerializer():

#    id = IntegerField(label='ID', read_only=True)

#    title = CharField(allow_blank=True, max_length=100, required=False)

#    code = CharField(style={'base_template': 'textarea.html'})

#    linenos = BooleanField(required=False)

#    language = ChoiceField(choices=[('Clipper', 'FoxPro'), ('Cucumber', 'Gherkin'), ('RobotFramework', 'RobotFramework'), ('abap', 'ABAP'), ('ada', 'Ada')...

#    style = ChoiceField(choices=[('autumn', 'autumn'), ('borland', 'borland'), ('bw', 'bw'), ('colorful', 'colorful')...

ModelSerializer 클래스는 특히 마술 같은 것을하지 않는다는 것을 기억하는 것이 중요합니다. 클래스는 간단히 serializer 클래스를 만드는 바로 가기입니다.

·         자동으로 결정된 필드 집합

·         create() update() 메소드에 대한 간단한 기본 구조

Writing regular Django views using our Serializer

새로운 Serializer 클래스를 사용하여 API 뷰를 작성하는 방법을 살펴 보자. 잠시 동안 우리는 REST 프레임 워크의 다른 기능을 사용하지 않을 것이고, 우리는 보기를 일반적인 Django보기로 작성하게 것이다.

우리는 json으로 반환하는 데이터를 렌더링하는 사용할 있는 HttpResponse 하위 클래스를 만드는 것으로 시작합니다.

snippets/views.py 파일을 편집하고 다음을 추가하십시오.

from django.http import HttpResponse

from django.views.decorators.csrf import csrf_exempt

from rest_framework.renderers import JSONRenderer

from rest_framework.parsers import JSONParser

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

 

class JSONResponse(HttpResponse):

    """

    An HttpResponse that renders its content into JSON.

    """

    def __init__(self, data, **kwargs):

        content = JSONRenderer().render(data)

        kwargs['content_type'] = 'application/json'

        super(JSONResponse, self).__init__(content, **kwargs)

API 루트는 모든 기존 스니펫을 나열하거나 새로운 스니펫을 만드는 것을 지원하는 보기가 것입니다.

@csrf_exempt

def snippet_list(request):

    """

    List all code snippets, or create a new snippet.

    """

    if request.method == 'GET':

        snippets = Snippet.objects.all()

        serializer = SnippetSerializer(snippets, many=True)

        return JSONResponse(serializer.data)

 

    elif request.method == 'POST':

        data = JSONParser().parse(request)

        serializer = SnippetSerializer(data=data)

        if serializer.is_valid():

            serializer.save()

            return JSONResponse(serializer.data, status=201)

        return JSONResponse(serializer.errors, status=400)

CSRF 토큰이 없는 클라이언트로부터 보기로 POST 있기를 원한다면 보기를 csrf_exempt 표시해야 합니다. 이것은 당신이 일반적으로 하고 싶어하는 것이 아니며, REST 프레임 워크 뷰는 실제로 이것보다 현명한 동작을 사용하지만, 지금 우리의 목적을 위해 것입니다.

또한 개별 스니펫에 해당하는 보기가 필요하며 스니펫을 검색, 업데이트 또는 삭제하는 사용할 있습니다.

@csrf_exempt

def snippet_detail(request, pk):

    """

    Retrieve, update or delete a code snippet.

    """

    try:

        snippet = Snippet.objects.get(pk=pk)

    except Snippet.DoesNotExist:

        return HttpResponse(status=404)

 

    if request.method == 'GET':

        serializer = SnippetSerializer(snippet)

        return JSONResponse(serializer.data)

 

    elif request.method == 'PUT':

        data = JSONParser().parse(request)

        serializer = SnippetSerializer(snippet, data=data)

        if serializer.is_valid():

            serializer.save()

            return JSONResponse(serializer.data)

        return JSONResponse(serializer.errors, status=400)

 

    elif request.method == 'DELETE':

        snippet.delete()

        return HttpResponse(status=204)

마지막으로 이러한 뷰를 연결해야 합니다. snippets/urls.py 파일을 만듭니다.

from django.conf.urls import url

from snippets import views

 

urlpatterns = [

    url(r'^snippets/$', views.snippet_list),

    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),

]

또한 tutorial/urls.py 파일의 urlconf 루트를 연결하여 스니펫 앱의 URL 포함시켜야 합니다.

from django.conf.urls import url, include

 

urlpatterns = [

    url(r'^', include('snippets.urls')),

]

우리가 현재 적절히 다루지 않는 가지 뚜렷한 경우가 있다는 점은 주목할 가치가 있습니다. 형식이 잘못된 json 보내거나 보기에서 처리할 없는 메서드로 요청을 보내면 500 "서버 오류" 응답이 표시됩니다. 그러나, 지금 당장 할거야.

Testing our first attempt at a Web API

이제 스니펫을 제공하는 샘플 서버를 시작할 있습니다.

밖으로 나가 ...

quit()

... 그리고, 장고의 개발 서버를 시작하십시오.

python manage.py runserver

 

Validating models...

 

0 errors found

Django version 1.8.3, using settings 'tutorial.settings'

Development server is running at http://127.0.0.1:8000/

Quit the server with CONTROL-C.

다른 터미널 창에서 서버를 테스트할 있습니다.

curl 또는 httpie 사용하여 API 테스트할 있습니다. Httpie Python으로 작성된 사용자 친화적인 http 클라이언트입니다. 그것을 설치합시다.

pip 사용하여 httpie 설치할 있습니다.

pip install httpie

마지막으로 모든 스니펫 목록을 얻을 있습니다.

http http://127.0.0.1:8000/snippets/

 

HTTP/1.1 200 OK

...

[

  {

    "id": 1,

    "title": "",

    "code": "foo = \"bar\"\n",

    "linenos": false,

    "language": "python",

    "style": "friendly"

  },

  {

    "id": 2,

    "title": "",

    "code": "print \"hello, world\"\n",

    "linenos": false,

    "language": "python",

    "style": "friendly"

  }

]

또는 해당 ID 참조하여 특정 스니펫을 가져올 있습니다.

http http://127.0.0.1:8000/snippets/2/

 

HTTP/1.1 200 OK

...

{

  "id": 2,

  "title": "",

  "code": "print \"hello, world\"\n",

  "linenos": false,

  "language": "python",

  "style": "friendly"

}

마찬가지로 브라우저에서 URL 방문하여 동일한 json 표시할 있습니다.

Where are we now

우리는 지금까지 괜찮습니다. 우리는 Django Forms API 유사한 직렬화 API 가지고 있습니다. 그리고 가지 일반적인 Django 뷰가 있습니다.

우리의 API 뷰는 json 응답을 제공하는 이상으로 특별한 순간에는 특별한 일을 하지 않습니다. 우리가 여전히 정리하기를 원하는 오류 처리가 있습니다. 하지만 작동하는 API입니다.

튜토리얼의 파트 2에서 어떻게 개선할 있는지 보겠습니다.


Tutorial 2: Requests and Responses

시점부터 REST 프레임 워크의 핵심을 다루기 시작합니다. 가지 필수 빌딩 블록을 소개합시다.

Request objects

REST 프레임 워크는 일반 HttpRequest 확장하고 보다 유연한 요청 구문 분석을 제공하는 Request 객체를 도입합니다. Request 객체의 핵심 기능은 request.POST 비슷한 request.data 속성이지만 API 작업에 유용합니다.

request.POST  # Only handles form data.  Only works for 'POST' method.

request.data  # Handles arbitrary data.  Works for 'POST', 'PUT' and 'PATCH' methods.

Response objects

REST 프레임 워크는 렌더링되지 않은 컨텐트를 취하고 컨텐트 협상을 사용하여 클라이언트에 반환할 올바른 컨텐트 유형을 결정하는 TemplateResponse 유형인 Response 객체도 도입합니다.

return Response(data)  # Renders to content type as requested by the client.

Status codes

뷰에서 숫자 HTTP 상태 코드를 사용한다고 해서 분명히 읽을 있는 것은 아니며, 오류 코드가 잘못되었을 경우 통지하지 않는 것이 쉽습니다. REST 프레임 워크는 상태 모듈의 HTTP_400_BAD_REQUEST 같은 상태 코드에 대해 보다 명확한 식별자를 제공합니다. 숫자 식별자를 사용하는 대신 전체적으로 이것을 사용하는 것이 좋습니다.

Wrapping API views

REST 프레임 워크는 API보기를 작성하는 사용할 수있는 개의 래퍼를 제공합니다.

1. 함수 기반 views 작업하기 위한 @api_view 데코레이터.

2. 클래스 기반 작업을 위한 APIView 클래스.

이러한 래퍼는 보기에서 요청 인스턴스를 수신하고 Response 객체에 컨텍스트를 추가하여 컨텐트 협상을 수행할 있도록 가지 기능을 제공합니다.

래퍼는 적절할 405 Method Not Allowed 응답을 반환하고 잘못된 입력으로 request.data 액세스할 발생하는 ParseError 예외를 처리하는 등의 동작도 제공합니다.

Pulling it all together

, 새로운 구성 요소를 사용하여 가지 보기를 작성해 보겠습니다.

우리는 이상 views.py에서 JSONResponse 클래스가 필요하지 않으므로 삭제하고 삭제하십시오. 끝나면 우리의 견해를 약간 리팩토링할 있습니다.

from rest_framework import status

from rest_framework.decorators import api_view

from rest_framework.response import Response

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

 

 

@api_view(['GET', 'POST'])

def snippet_list(request):

    """

    List all snippets, or create a new snippet.

    """

    if request.method == 'GET':

        snippets = Snippet.objects.all()

        serializer = SnippetSerializer(snippets, many=True)

        return Response(serializer.data)

 

    elif request.method == 'POST':

        serializer = SnippetSerializer(data=request.data)

        if serializer.is_valid():

            serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

우리의 인스턴스 뷰는 앞의 예제보다 향상되었습니다. 간결해졌으며 이제 Forms API 작업하는 것과 매우 유사합니다. 우리는 또한 응답의 의미를 보다 분명하게 주는 지명된 상태 코드를 사용하고 있습니다.

다음은 views.py 모듈에 있는 개별 스니펫의 보기입니다.

@api_view(['GET', 'PUT', 'DELETE'])

def snippet_detail(request, pk):

    """

    Retrieve, update or delete a snippet instance.

    """

    try:

        snippet = Snippet.objects.get(pk=pk)

    except Snippet.DoesNotExist:

        return Response(status=status.HTTP_404_NOT_FOUND)

 

    if request.method == 'GET':

        serializer = SnippetSerializer(snippet)

        return Response(serializer.data)

 

    elif request.method == 'PUT':

        serializer = SnippetSerializer(snippet, data=request.data)

        if serializer.is_valid():

            serializer.save()

            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

 

    elif request.method == 'DELETE':

        snippet.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

이것은 모두 매우 익숙해야 합니다 - 그것은 일반적인 Django 뷰를 사용하는 것과 많이 다르지 않습니다.

우리는 이상 특정 콘텐츠 유형에 대한 요청이나 응답을 명시적으로 묶어 두지 않습니다. request.data 들어오는 json 요청을 처리할 있지만 다른 형식도 처리할 있습니다. 마찬가지로 데이터로 응답 객체를 반환하지만 REST 프레임 워크가 응답을 올바른 콘텐츠 유형으로 렌더링하도록 허용합니다.

Adding optional format suffixes to our URLs

Google 응답이 이상 단일 콘텐츠 유형에 고정되어 있지 않다는 사실을 이용하려면 API 접미사에 형식 접미사에 대한 지원을 추가하십시오. 형식 접미어를 사용하면 명시적으로 지정된 형식을 참조하는 URL 제공되므로 API http://example.com/api/items/4.json 같은 URL 처리할 있음을 의미합니다.

가지 보기 모두에 형식 키워드 인수를 추가하는 것으로 시작하십시오.

def snippet_list(request, format=None):

def snippet_detail(request, pk, format=None):

이제 urls.py 파일을 약간 업데이트하여 기존 URL 외에도 format_suffix_patterns 세트를 추가하십시오.

from django.conf.urls import url

from rest_framework.urlpatterns import format_suffix_patterns

from snippets import views

 

urlpatterns = [

    url(r'^snippets/$', views.snippet_list),

    url(r'^snippets/(?P<pk>[0-9]+)$', views.snippet_detail),

]

 

urlpatterns = format_suffix_patterns(urlpatterns)

이러한 추가 URL 패턴을 반드시 추가할 필요는 없지만 특정 형식을 간단하고 명확하게 참조할 있습니다.

How's it looking?

자습서 파트 1에서 했던 것처럼 command line에서 API 테스트하십시오. 유효하지 않은 요청을 보내면 모든 오류 처리가 처리됩니다.

이전과 마찬가지로 모든 스니펫 목록을 얻을 있습니다.

http http://127.0.0.1:8000/snippets/

 

HTTP/1.1 200 OK

...

[

  {

    "id": 1,

    "title": "",

    "code": "foo = \"bar\"\n",

    "linenos": false,

    "language": "python",

    "style": "friendly"

  },

  {

    "id": 2,

    "title": "",

    "code": "print \"hello, world\"\n",

    "linenos": false,

    "language": "python",

    "style": "friendly"

  }

]

Accept 헤더를 사용하여 응답의 형식을 제어 있습니다.

http http://127.0.0.1:8000/snippets/ Accept:application/json  # Request JSON

http http://127.0.0.1:8000/snippets/ Accept:text/html         # Request HTML

또는 형식 접미사를 추가하여

http http://127.0.0.1:8000/snippets.json  # JSON suffix

http http://127.0.0.1:8000/snippets.api   # Browsable API suffix

마찬가지로 Content-Type 헤더를 사용하여 보내는 요청의 형식을 제어할 있습니다.

# POST using form data

http --form POST http://127.0.0.1:8000/snippets/ code="print 123"

 

{

  "id": 3,

  "title": "",

  "code": "print 123",

  "linenos": false,

  "language": "python",

  "style": "friendly"

}

 

# POST using JSON

http --json POST http://127.0.0.1:8000/snippets/ code="print 456"

 

{

    "id": 4,

    "title": "",

    "code": "print 456",

    "linenos": false,

    "language": "python",

    "style": "friendly"

}

위의 http 요청에 --debug 스위치를 추가하면 요청 헤더에서 요청 유형을 있습니다.

이제 브라우저에서 API 열고 http://127.0.0.1:8000/snippets/ 방문하십시오.

Browsability

API 클라이언트 요청에 따라 응답의 콘텐츠 형식을 선택하기 때문에 기본적으로 브라우저에서 해당 리소스를 요청할 HTML 형식의 리소스 표현을 반환합니다. 이를 통해 API 웹에서 검색 가능한 HTML 표현을 반환할 있습니다.

브라우저 API 사용하면 사용 편의성이 크게 향상되며 API 훨씬 쉽게 개발하고 사용할 있습니다. 또한 API 검사하고 작업하려는 다른 개발자의 진입 장벽을 크게 낮춥니다.

찾아보기 가능한 API 기능 사용자 정의 방법에 대한 자세한 내용은 탐색 가능한 API 항목을 참조하십시오.

What's next?

튜토리얼 파트 3에서는 클래스 기반 뷰를 사용하고 일반적인 뷰가 작성해야 하는 코드 양을 줄이는 방법을 살펴 보겠습니다.


Tutorial 3: Class-based Views

함수 기반 보기가 아닌 클래스 기반 보기를 사용하여 API보기를 작성할 수도 있습니다. 여기서 있듯이 코드는 일반적인 기능을 재사용할 있는 강력한 패턴이며 DRY(Don't repeat yourself) 코드로 유지하는 도움이 됩니다.

Rewriting our API using class-based views

먼저 루트 보기를 클래스 기반 보기로 다시 작성합니다. 모든 것이 views.py 리팩토링입니다.

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

from django.http import Http404

from rest_framework.views import APIView

from rest_framework.response import Response

from rest_framework import status

 

 

class SnippetList(APIView):

    """

    List all snippets, or create a new snippet.

    """

    def get(self, request, format=None):

        snippets = Snippet.objects.all()

        serializer = SnippetSerializer(snippets, many=True)

        return Response(serializer.data)

 

    def post(self, request, format=None):

        serializer = SnippetSerializer(data=request.data)

        if serializer.is_valid():

            serializer.save()

            return Response(serializer.data, status=status.HTTP_201_CREATED)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

여태까지는 그런대로 잘됐다. 이전의 경우와 매우 비슷해 보였지만 다른 HTTP 메소드를 구분할 있습니다. 우리는 또한 views.py에서 인스턴스 뷰를 업데이트해야 합니다.

class SnippetDetail(APIView):

    """

    Retrieve, update or delete a snippet instance.

    """

    def get_object(self, pk):

        try:

            return Snippet.objects.get(pk=pk)

        except Snippet.DoesNotExist:

            raise Http404

 

    def get(self, request, pk, format=None):

        snippet = self.get_object(pk)

        serializer = SnippetSerializer(snippet)

        return Response(serializer.data)

 

    def put(self, request, pk, format=None):

        snippet = self.get_object(pk)

        serializer = SnippetSerializer(snippet, data=request.data)

        if serializer.is_valid():

            serializer.save()

            return Response(serializer.data)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

 

    def delete(self, request, pk, format=None):

        snippet = self.get_object(pk)

        snippet.delete()

        return Response(status=status.HTTP_204_NO_CONTENT)

좋아 보인다. 다시 말하지만, 여전히 함수 기반 뷰와 매우 유사합니다.

클래스 기반 뷰를 사용하고 있기 때문에 urls.py 약간 리팩토링해야 합니다.

from django.conf.urls import url

from rest_framework.urlpatterns import format_suffix_patterns

from snippets import views

 

urlpatterns = [

    url(r'^snippets/$', views.SnippetList.as_view()),

    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view()),

]

 

urlpatterns = format_suffix_patterns(urlpatterns)

좋아, 끝났어. 개발 서버를 실행하면 모든 것이 이전과 같이 작동해야 합니다.

Using mixins

클래스 기반 뷰를 사용하여 얻은 가장 장점 하나는 재사용 가능한 비트를 쉽게 구성할 있다는 것입니다.

지금까지 사용해 생성/검색/업데이트/삭제 작업은 모델에서 지원하는 모든 API 지원 보기와 매우 유사합니다. 이러한 공통된 동작은 REST 프레임 워크의 mixin 클래스에서 구현됩니다.

mixin 클래스를 사용하여 뷰를 구성하는 방법을 살펴 보겠습니다. 여기에 우리의 views.py 모듈이 있습니다.

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

from rest_framework import mixins

from rest_framework import generics

 

class SnippetList(mixins.ListModelMixin,

                  mixins.CreateModelMixin,

                  generics.GenericAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

 

    def get(self, request, *args, **kwargs):

        return self.list(request, *args, **kwargs)

 

    def post(self, request, *args, **kwargs):

        return self.create(request, *args, **kwargs)

우리는 잠시 시간을 내어 여기에서 어떤 일이 일어나고 있는지 정확하게 살펴볼 것입니다. GenericAPIView 사용하고 ListModelMixin CreateModelMixin 추가하여 뷰를 작성합니다.

기본 클래스는 핵심 기능을 제공하고 mixin 클래스는 .list () .create () 액션을 제공합니다. 그런 다음 get post 메소드를 명시적으로 적절한 액션에 바인딩합니다. 지금까지 단순한 물건.

 

class SnippetDetail(mixins.RetrieveModelMixin,

                    mixins.UpdateModelMixin,

                    mixins.DestroyModelMixin,

                    generics.GenericAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

 

    def get(self, request, *args, **kwargs):

        return self.retrieve(request, *args, **kwargs)

 

    def put(self, request, *args, **kwargs):

        return self.update(request, *args, **kwargs)

 

    def delete(self, request, *args, **kwargs):

        return self.destroy(request, *args, **kwargs)

비슷합니다. GenericAPIView 클래스를 사용하여 핵심 기능을 제공하고 .retrieve (), .update () .destroy () 액션을 제공하기 위해 mixin 추가합니다.

Using generic class-based views

mixin 클래스를 사용하여 이전보다 약간 적은 코드를 사용하기 위해 뷰를 다시 작성했지만 걸음 나아갈 있습니다. REST 프레임 워크는 이미 혼합된 일반 집합을 제공하여 우리의 views.py 모듈을 많이 트리밍할 있습니다.

from snippets.models import Snippet

from snippets.serializers import SnippetSerializer

from rest_framework import generics

 

 

class SnippetList(generics.ListCreateAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

 

 

class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

와우, 그건 간결합니다. 우리는 엄청난 돈을 무료로 받았고, 코드는 훌륭하고 깨끗하며 관용적인 장고처럼 보입니다.

다음으로 튜토리얼의 4부로 넘어갈 것이다. 여기서 우리는 API 대한 인증과 권한을 어떻게 다룰 있는지 살펴볼 것이다.


Tutorial 4: Authentication & Permissions

현재 Google API에는 코드 스니펫을 편집하거나 삭제할 있는 사람에 대한 제한이 없습니다. 다음을 확실히 하기 위해 발전된 행동을 원합니다.

·         코드 스니펫은 항상 제작자와 연결됩니다.

·         인증된 사용자만 스니펫을 만들 있습니다.

·         스니펫 작성자만 업데이트 또는 삭제할 있습니다.

·         인증되지 않은 요청에는 완전한 읽기 전용 액세스 권한이 있어야 합니다.

Adding information to our model

우리는 Snippet 모델 클래스에 가지 변경을 가할 것입니다. 먼저 개의 필드를 추가해 보겠습니다. 필드 하나는 코드 스니펫을 만든 사용자를 나타내는 사용됩니다. 다른 필드는 코드의 강조 표시된 HTML 표현을 저장하는 사용됩니다.

models.py Snippet 모델에 다음 필드를 추가하십시오.

owner = models.ForeignKey('auth.User', related_name='snippets', on_delete=models.CASCADE)

highlighted = models.TextField()

또한 모델이 저장될 pygments 코드 강조 라이브러리를 사용하여 강조 표시된 필드가 채워지도록 해야 합니다.

추가 import들이 필요합니다.

from pygments.lexers import get_lexer_by_name

from pygments.formatters.html import HtmlFormatter

from pygments import highlight

이제 모델 클래스에 .save () 메소드를 추가할 있습니다.

def save(self, *args, **kwargs):

    """

    Use the `pygments` library to create a highlighted HTML

    representation of the code snippet.

    """

    lexer = get_lexer_by_name(self.language)

    linenos = self.linenos and 'table' or False

    options = self.title and {'title': self.title} or {}

    formatter = HtmlFormatter(style=self.style, linenos=linenos,

                              full=True, **options)

    self.highlighted = highlight(self.code, lexer, formatter)

    super(Snippet, self).save(*args, **kwargs)

모든 작업이 완료되면 데이터베이스 테이블을 업데이트해야 합니다. 일반적으로 데이터베이스 마이그 레이션을 생성하기 위해 튜토리얼에서 데이터베이스를 삭제하고 다시 시작해 보겠습니다.

rm -f tmp.db db.sqlite3

rm -r snippets/migrations

python manage.py makemigrations snippets

python manage.py migrate

또한 가지 사용자를 만들어 API 테스트에 사용할 수도 있습니다. 이를 수행하는 가장 빠른 방법은 createsuperuser 명령을 사용하는 것입니다.

python manage.py createsuperuser

Adding endpoints for our User models

이제 일부 사용자가 작업할 있게 되었으므로 해당 사용자의 표현을 API 추가하는 것이 좋습니다. 새로운 시리얼라이저를 만드는 것은 쉽습니다. serializers.py 추가 :

from django.contrib.auth.models import User

 

class UserSerializer(serializers.ModelSerializer):

    snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())

 

    class Meta:

        model = User

        fields = ('id', 'username', 'snippets')

'snippets' User 모델에서 역의 관계이므로 ModelSerializer 클래스를 사용할 기본적으로 포함되지 않으므로 명시적 필드를 추가해야 했습니다.

우리는 또한 views.py 가지 보기를 추가할 것입니다. 우리는 단지 사용자 표현에 대해 읽기 전용 뷰를 사용하고자 하므로 ListAPIView RetrieveAPIView 제네릭 클래스 기반 뷰를 사용합니다.

from django.contrib.auth.models import User

 

 

class UserList(generics.ListAPIView):

    queryset = User.objects.all()

    serializer_class = UserSerializer

 

 

class UserDetail(generics.RetrieveAPIView):

    queryset = User.objects.all()

    serializer_class = UserSerializer

UserSerializer 클래스도 import해야 합니다.

from snippets.serializers import UserSerializer

마지막으로 URL conf에서 URL 참조하여 해당 보기를 API 추가해야 합니다. urls.py 패턴에 다음을 추가하십시오.

url(r'^users/$', views.UserList.as_view()),

url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),

Associating Snippets with Users

현재 코드 스니펫을 만든 경우 스니펫을 만든 사용자를 스니펫 인스턴스와 연결할 없습니다. 사용자는 직렬화된 표현의 일부로 전송되지 않고 들어오는 요청의 특성을 대신합니다.

우리가 처리하는 방법은 스니펫 뷰에서 .perform_create () 메서드를 재정의하여 인스턴스 저장을 관리하는 방법을 수정하고 들어오는 요청이나 요청 URL 암시적으로 포함된 정보를 처리하는 것입니다.

SnippetList 클래스에서 다음 메서드를 추가하십시오.

def perform_create(self, serializer):

    serializer.save(owner=self.request.user)

serializer create () 메소드에는 요청의 유효성이 검사된 데이터와 함께 추가적인 'owner'필드가 전달됩니다.

Updating our serializer

스니펫이 스니펫을 생성한 사용자와 연결되었으므로 이를 반영하도록 SnippetSerializer 업데이트합시다. serializers.py serializer 정의에 다음 필드를 추가합니다.

owner = serializers.ReadOnlyField(source='owner.username')

참고 : 내부 메타 클래스의 필드 목록에 'owner' 추가해야 합니다.

분야는 흥미로운 일을 하고 있습니다. source 인수는 필드를 채우는 사용되는 속성을 제어하며 직렬화된 인스턴스의 모든 속성을 지정할 있습니다. 또한 위의 점으로 구분된 표기법을 사용할 수도 있습니다. 경우 Django 템플릿 언어에서 사용되는 것과 비슷한 방식으로 지정된 속성을 주의깊게 음미합니다.

우리가 추가한 필드는 타입이 지정되지 않은 ReadOnlyField 클래스입니다. CharField, BooleanField 등과 같은 다른 유형의 필드와는 대조적입니다. 타입이 지정되지 않은 ReadOnlyField 항상 읽기 전용이며 직렬화된 표현에 사용되지만 실제로는 사용되지 않습니다. deserialize 모델 인스턴스를 업데이트하는 사용됩니다. 여기서 CharField(read_only=True) 사용할 있습니다.

Adding required permissions to views

코드 스니펫이 사용자와 연결되어 있으므로 인증된 사용자만 코드 스니펫을 생성, 업데이트 삭제할 있습니다.

REST 프레임 워크에는 주어진 뷰에 액세스할 있는 사용자를 제한하는 사용할 있는 많은 권한 클래스가 포함되어 있습니다. 경우 IsAuthenticatedOrReadOnly 사용하면 인증된 요청에 읽기 - 쓰기 액세스 권한이 부여되고 인증되지 않은 요청에는 읽기 전용 권한이 부여됩니다.

먼저 views 모듈에 다음 가져 오기를 추가하십시오.

from rest_framework import permissions

그런 다음 SnippetList SnippetDetail 클래스 모두에 다음 특성을 추가하십시오.

permission_classes = (permissions.IsAuthenticatedOrReadOnly,)

Adding login to the Browsable API

현재 브라우저를 열고 탐색 가능한 API 이동하면 이상 코드 스니펫을 만들 없다는 것을 알게 됩니다. 그렇게 하기 위해 우리는 사용자로 로그인 있어야 합니다.

프로젝트 레벨 urls.py 파일의 URLconf 편집하여 브라우저 API 사용할 로그인 보기를 추가 있습니다.

파일 위에 다음 가져 오기를 추가하십시오.

from django.conf.urls import include

그리고, 파일 끝에는 탐색 가능한 API 대한 로그인 로그 아웃보기를 포함하는 패턴을 추가하십시오.

urlpatterns += [

    url(r'^api-auth/', include('rest_framework.urls',

                               namespace='rest_framework')),

]

패턴의 r '^ api-auth /' 부분은 실제로 사용하고자 하는 URL 있습니다. 유일한 제약은 포함된 URL 'rest_framework' 네임 스페이스를 사용해야 한다는 것입니다. Django 1.9 이상에서는 REST 프레임 워크가 네임 스페이스를 설정하므로 생략할 있습니다.

이제 브라우저를 다시 열고 페이지를 새로 고침하면 페이지 오른쪽 상단에 '로그인' 링크가 표시됩니다. 이전에 만든 사용자 하나로 로그인하면 코드 스니펫을 다시 만들 있습니다.

가지 코드 스니펫을 만든 '/ users /' 끝점으로 이동하여 사용자의 '스니펫' 입력란에 사용자와 연결된 스니펫 ID 목록이 표시되는지 확인합니다.

Object level permissions

모든 코드 스니펫을 누구나 있게 하고 싶지만 코드 스니펫을 만든 사용자만 업데이트하거나 삭제할 있습니다.

이를 위해 사용자 지정 권한을 만들어야 합니다.

스니펫 앱에서 permissions.py라는 파일을 만듭니다.

from rest_framework import permissions

 

 

class IsOwnerOrReadOnly(permissions.BasePermission):

    """

    Custom permission to only allow owners of an object to edit it.

    """

 

    def has_object_permission(self, request, view, obj):

        # Read permissions are allowed to any request,

        # so we'll always allow GET, HEAD or OPTIONS requests.

        if request.method in permissions.SAFE_METHODS:

            return True

 

        # Write permissions are only allowed to the owner of the snippet.

        return obj.owner == request.user

이제 SnippetDetail 클래스의 permission_classes 속성을 편집하여 해당 스니펫 인스턴스 끝점에 해당 사용자 지정 권한을 추가할 있습니다.

permission_classes = (permissions.IsAuthenticatedOrReadOnly,

                      IsOwnerOrReadOnly,)

IsOwnerOrReadOnly 클래스도 가져와야 합니다.

from snippets.permissions import IsOwnerOrReadOnly

이제 브라우저를 다시 열면 코드 스니펫을 만든 사용자와 동일한 사용자로 로그인한 경우 'DELETE' 'PUT'작업만 스니펫 인스턴스 끝점에 나타납니다.

Authenticating with the API

이제는 API 대한 사용 권한 집합이 있으므로 스니펫을 편집하려면 API 대한 요청을 인증해야 합니다. 우리는 어떤 인증 클래스도 설정하지 않았기 때문에 현재 SessionAuthentication BasicAuthentication이라는 기본값이 적용되어 있습니다.

브라우저를 통해 API 상호 작용할 우리는 로그인할 있으며 브라우저 세션은 요청에 대해 필요한 인증을 제공합니다.

프로그래밍 방식으로 API 상호 작용할 경우 요청에 대해 인증 자격 증명을 명시적으로 제공해야 합니다.

인증 없이 스니펫을 만들려고 하면 오류가 발생합니다.

http POST http://127.0.0.1:8000/snippets/ code="print 123"

 

{

    "detail": "Authentication credentials were not provided."

}

이전에 생성한 사용자 하나의 사용자 이름과 비밀번호를 포함시켜 요청을 성공적으로 처리 있습니다.

http -a tom:password123 POST http://127.0.0.1:8000/snippets/ code="print 789"

 

{

    "id": 5,

    "owner": "tom",

    "title": "foo",

    "code": "print 789",

    "linenos": false,

    "language": "python",

    "style": "friendly"

}

Summary

우리는 API 시스템 사용자 그들이 작성한 코드 스니펫에 대한 사용 권한을 상당히 세분화했습니다.

튜토리얼의 5부에서는 강조 표시된 스니펫의 HTML 끝점을 만들어 모든 것을 묶어 시스템에서 관계에 하이퍼 링크를 사용하여 API 응집력을 향상시키는 방법을 살펴 보겠습니다.


Tutorial 5: Relationships & Hyperlinked APIs

현재 API 내의 관계는 기본 키를 사용하여 표현됩니다. 이번 튜토리얼에서는 관계에 하이퍼 링크를 사용하는 대신 API 응집력과 발견 가능성을 향상시킬 것입니다.

Creating an endpoint for the root of our API

지금은 '스니펫' '사용자' 대한 끝점이 있지만 API 대한 단일 진입점은 없습니다. 이를 만들기 위해 우리는 정규 함수 기반 뷰와 앞서 소개 @api_view 데코레이터를 사용합니다. snippets/views.py 추가:

from rest_framework.decorators import api_view

from rest_framework.response import Response

from rest_framework.reverse import reverse

 

 

@api_view(['GET'])

def api_root(request, format=None):

    return Response({

        'users': reverse('user-list', request=request, format=format),

        'snippets': reverse('snippet-list', request=request, format=format)

    })

여기서 가지를 알아야 합니다. 첫째, 정규화된 URL 반환하기 위해 REST 프레임 워크의 reverse 함수를 사용하고 있습니다. 번째로, URL 패턴은 나중에 snippets/urls.py에서 선언할 편의 이름으로 식별됩니다.

Creating an endpoint for the highlighted snippets

아직 pastebin API에서 빠져있는 다른 명백한 점은 끝점을 강조 표시하는 코드입니다.

다른 모든 API 엔드 포인트와 달리 JSON 사용하지 않고 대신 HTML 표현만 제공합니다. REST 프레임 워크에는 가지 스타일의 HTML 렌더러가 있습니다. 하나는 템플리트를 사용하여 렌더링된 HTML 처리하는 것이고, 다른 하나는 사전 렌더링된 HTML 처리하는 것입니다. 번째 렌더러는 엔드 포인트에 사용하려는 렌더러입니다.

코드 하이라이트 뷰를 생성할 고려해야 다른 사항은 우리가 사용할 있는 기존의 구체적인 일반 뷰가 없다는 것입니다. 우리는 객체 인스턴스가 아니라 객체 인스턴스의 속성을 리턴합니다.

구체적인 일반 뷰를 사용하는 대신 인스턴스를 나타내는 기본 클래스를 사용하고 자체 .get () 메서드를 만듭니다. snippets/views.py 추가 :

from rest_framework import renderers

from rest_framework.response import Response

 

class SnippetHighlight(generics.GenericAPIView):

    queryset = Snippet.objects.all()

    renderer_classes = (renderers.StaticHTMLRenderer,)

 

    def get(self, request, *args, **kwargs):

        snippet = self.get_object()

        return Response(snippet.highlighted)

평소처럼 우리가 생성한 새로운 뷰를 URLconf 추가해야 합니다. snippets/urls.py API 루트에 대한 URL 패턴을 추가합니다.

url(r'^$', views.api_root),

그리고 나서 스니펫 하이라이트에 대한 URL 패턴을 추가하십시오.

url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),

Hyperlinking our API

엔티티 간의 관계를 다루는 것은 API 디자인의 더욱 어려운 측면 하나입니다. 관계를 나타내기 위해 선택할 있는 여러 가지 방법이 있습니다.

·         기본 사용

·         엔티티 간의 하이퍼 링크 사용

·         관련 엔티티에서 고유 식별 슬러그 필드 사용

·         관련 엔티티의 기본 문자열 표현 사용

·         부모 표현 내에서 관련된 엔티티의 중첩

·         다른 사용자 지정 표현

REST 프레임 워크는 이러한 모든 스타일을 지원하며 정방향 또는 역방향 관계에 적용하거나 일반 외래 키와 같은 사용자 지정 관리자에 적용할 있습니다.

경우 엔티티간에 하이퍼 링크된 스타일을 사용하고 싶습니다. 이렇게 하기 위해 우리는 기존 ModelSerializer 대신 HyperlinkedModelSerializer 확장하기 위해 serializer 수정합니다.

HyperlinkedModelSerializer에는 ModelSerializer 다음과 같은 차이점이 있습니다.

·         기본적으로 id 필드는 포함되지 않습니다.

·         HyperlinkedIdentityField 사용하여 URL 필드를 포함합니다.

·         관계는 PrimaryKeyRelatedField 대신 HyperlinkedRelatedField 사용합니다.

하이퍼 링크를 사용하기 위해 기존의 serializer 쉽게 다시 작성할 있습니다. snippets/serializers.py 다음을 추가합니다.

class SnippetSerializer(serializers.HyperlinkedModelSerializer):

    owner = serializers.ReadOnlyField(source='owner.username')

    highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

 

    class Meta:

        model = Snippet

        fields = ('url', 'id', 'highlight', 'owner',

                  'title', 'code', 'linenos', 'language', 'style')

 

 

class UserSerializer(serializers.HyperlinkedModelSerializer):

    snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True)

 

    class Meta:

        model = User

        fields = ('url', 'id', 'username', 'snippets')

새로운 '강조 표시' 필드도 추가했습니다. 필드는 'snippet-detail' URL 패턴 대신 'snippet-highlight' url 패턴을 가리키는 점을 제외하면 url 필드와 동일한 유형입니다.

'.json' 같은 접미사 형식의 URL 포함했기 때문에 하이라이트 필드에 접미사가 있는 형식의 접미어 하이퍼 링크에 '.html'접미사를 사용해야 한다는 것을 나타내야 합니다.

Making sure our URL patterns are named

하이퍼 링크된 API 사용하려면 URL 패턴의 이름을 지정해야 합니다. 이름을 지정해야 하는 URL 패턴을 살펴 보겠습니다.

·         API 루트는 ‘user-list’ ‘snippet-list’ 참조합니다.

·         스니펫 serializer ‘snippet-highlight’ 참조하는 필드를 포함합니다.

·         사용자 serializer에는 ‘snippet-detail’ 참조하는 필드가 있습니다.

·         스니펫 사용자 serializer에는 기본적으로 ‘{model_name}-detail’ 참조하는 ‘url’ 필드가 포함되며, 경우 ‘snippet-detail’ ‘user-detail’ 됩니다.

모든 이름을 URLconf 추가한 최종 snippets/urls.py파일은 다음과 같아야 합니다.

from django.conf.urls import url, include

from rest_framework.urlpatterns import format_suffix_patterns

from snippets import views

 

# API endpoints

urlpatterns = format_suffix_patterns([

    url(r'^$', views.api_root),

    url(r'^snippets/$',

        views.SnippetList.as_view(),

        name='snippet-list'),

    url(r'^snippets/(?P<pk>[0-9]+)/$',

        views.SnippetDetail.as_view(),

        name='snippet-detail'),

    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$',

        views.SnippetHighlight.as_view(),

        name='snippet-highlight'),

    url(r'^users/$',

        views.UserList.as_view(),

        name='user-list'),

    url(r'^users/(?P<pk>[0-9]+)/$',

        views.UserDetail.as_view(),

        name='user-detail')

])

 

# Login and logout views for the browsable API

urlpatterns += [

    url(r'^api-auth/', include('rest_framework.urls',

                               namespace='rest_framework')),

]

Adding pagination

사용자 코드 스니펫의 목록 보기는 많은 인스턴스를 반환할 있으므로 결과에 페이지 매김을 하고 API 클라이언트가 페이지를 단계별로 실행할 있도록 해야 합니다.

tutorial/settings.py 파일을 약간 수정하여 페이지 스타일을 사용하도록 기본 목록 스타일을 변경할 있습니다. 다음 설정을 추가하십시오.

REST_FRAMEWORK = {

    'PAGE_SIZE': 10

}

REST 프레임 워크의 설정은 모두 'REST_FRAMEWORK'이라는 이름의 단일 사전 설정으로 네임 스페이스로 지정되므로 다른 프로젝트 설정과 구분할 있습니다.

필요하다면 페이지 매김 스타일을 커스터마이징할 수도 있지만, 경우에는 기본값을 고수할 것입니다

Browsing the API

브라우저를 열고 탐색 가능한 API 이동하면 링크를 따라 간단하게 API 둘러 있습니다.

또한 스니펫 인스턴스에서 '강조 표시' 링크를 있어 강조 표시된 코드 HTML 표현으로 연결됩니다.

튜토리얼의 6 부에서는 ViewSets 라우터를 사용하여 API 빌드하는 필요한 코드 양을 줄이는 방법을 살펴 보겠습니다.


Tutorial 6: ViewSets & Routers

REST 프레임 워크에는 개발자가 API 상태 상호 작용을 모델링하는 집중할 있는 ViewSet 처리하기 위한 추상화가 포함되어 있으며 일반적인 규칙에 따라 URL 작성을 자동으로 처리할 있습니다.

ViewSet 클래스는 읽기 또는 업데이트와 같은 오퍼레이션을 제공하고 get 또는 put 같은 메소드 핸들러는 제공하지 않는다는 점을 제외하면 View 클래스와 거의 동일합니다.

ViewSet 클래스는 마지막 순간에 집합으로 인스턴스화될 , 일반적으로 URL conf 정의하는 복잡성을 처리하는 Router 클래스를 사용하여 메소드 핸들러 세트에만 바인딩됩니다.

Refactoring to use ViewSets

현재의 세트를 가져 와서 세트로 리팩터링합니다.

먼저 UserList UserDetail 뷰를 단일 UserViewSet으로 리팩터링합니다. 개의 뷰를 제거하고 하나의 클래스로 대체 있습니다.

from rest_framework import viewsets

 

class UserViewSet(viewsets.ReadOnlyModelViewSet):

    """

    This viewset automatically provides `list` and `detail` actions.

    """

    queryset = User.objects.all()

    serializer_class = UserSerializer

여기에서는 ReadOnlyModelViewSet 클래스를 사용하여 자동으로 기본 '읽기 전용' 작업을 제공합니다. 우리는 여전히 일반 뷰를 사용할 때와 마찬가지로 queryset serializer_class 특성을 정확하게 설정하지만 이상 개의 별도 클래스에 동일한 정보를 제공할 필요가 없습니다.

다음으로 SnippetList, SnippetDetail SnippetHighlight 클래스를 대체할 것입니다. 가지 뷰를 제거하고 다시 단일 클래스로 대체할 있습니다.

from rest_framework.decorators import detail_route

 

class SnippetViewSet(viewsets.ModelViewSet):

    """

    This viewset automatically provides `list`, `create`, `retrieve`,

    `update` and `destroy` actions.

 

    Additionally we also provide an extra `highlight` action.

    """

    queryset = Snippet.objects.all()

    serializer_class = SnippetSerializer

    permission_classes = (permissions.IsAuthenticatedOrReadOnly,

                          IsOwnerOrReadOnly,)

 

    @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])

    def highlight(self, request, *args, **kwargs):

        snippet = self.get_object()

        return Response(snippet.highlighted)

 

    def perform_create(self, serializer):

        serializer.save(owner=self.request.user)

이번에는 ModelViewSet 클래스를 사용하여 기본 읽기 쓰기 작업을 완료했습니다.

@detail_route 데코레이터를 사용하여 highlight라는 사용자 정의 액션을 만들었습니다. 데코레이터는 표준 create/update/delete 스타일에 맞지 않는 사용자 정의 엔드 포인트를 추가하는 사용될 있습니다.

@detail_route 데코레이터를 사용하는 사용자 지정 작업은 기본적으로 GET 요청에 응답합니다. POST 요청에 응답한 작업을 원한다면 methods 인수를 사용할 있습니다.

사용자 지정 작업의 URL 기본적으로 메서드 이름 자체에 따라 다릅니다. url 생성하는 방법을 변경하려면 url_path 데코레이터 키워드 인수로 포함 있습니다.

Binding ViewSets to URLs explicitly

핸들러 메서드는 URLConf 정의할 때만 액션에 바인딩 됩니다. 내부에서 진행중인 작업을 보려면 먼저 ViewSets에서 명시적으로 일련의 보기를 만듭니다.

urls.py 파일에서 우리는 ViewSet 클래스를 구체적인 세트에 바인딩합니다.

 

from snippets.views import SnippetViewSet, UserViewSet, api_root

from rest_framework import renderers

 

snippet_list = SnippetViewSet.as_view({

    'get': 'list',

    'post': 'create'

})

snippet_detail = SnippetViewSet.as_view({

    'get': 'retrieve',

    'put': 'update',

    'patch': 'partial_update',

    'delete': 'destroy'

})

snippet_highlight = SnippetViewSet.as_view({

    'get': 'highlight'

}, renderer_classes=[renderers.StaticHTMLRenderer])

user_list = UserViewSet.as_view({

    'get': 'list'

})

user_detail = UserViewSet.as_view({

    'get': 'retrieve'

})

http 메소드를 뷰에 대해 필요한 액션에 바인딩하여 ViewSet 클래스에서 여러 뷰를 생성하는 방법에 주목하십시오.

이제 리소스를 구체적인 보기로 묶었으므로 평소대로 URL conf 사용하여 보기를 등록할 있습니다.

urlpatterns = format_suffix_patterns([

    url(r'^$', api_root),

    url(r'^snippets/$', snippet_list, name='snippet-list'),

    url(r'^snippets/(?P<pk>[0-9]+)/$', snippet_detail, name='snippet-detail'),

    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', snippet_highlight, name='snippet-highlight'),

    url(r'^users/$', user_list, name='user-list'),

    url(r'^users/(?P<pk>[0-9]+)/$', user_detail, name='user-detail')

])

Using Routers

View 클래스보다는 ViewSet 클래스를 사용하기 때문에 실제로 URL 직접 디자인할 필요가 없습니다. 보기 URL 자원을 연결하는 규칙은 Router 클래스를 사용하여 자동으로 처리할 있습니다. 적절한 세트를 라우터에 등록하고 나머지는 그대로 두면 됩니다.

여기 re-wired urls.py 파일이 있습니다.

from django.conf.urls import url, include

from snippets import views

from rest_framework.routers import DefaultRouter

 

# Create a router and register our viewsets with it.

router = DefaultRouter()

router.register(r'snippets', views.SnippetViewSet)

router.register(r'users', views.UserViewSet)

 

# The API URLs are now determined automatically by the router.

# Additionally, we include the login URLs for the browsable API.

urlpatterns = [

    url(r'^', include(router.urls)),

    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))

]

세트를 라우터에 등록하는 것은 url 패턴을 제공하는 것과 유사합니다. 뷰의 URL 접두어와 세트 자체라는 가지 인수가 포함됩니다.

우리가 사용하고 있는 DefaultRouter 클래스는 자동으로 API 루트 뷰를 생성하기 때문에 모듈에서 api_root 메소드를 삭제할 있습니다.

Trade-offs between views vs viewsets

세트를 사용하면 정말 유용한 추상화가 있습니다. API 통해 URL 규칙이 일관되게 유지되고 작성해야 하는 코드의 양을 최소화하며 URL conf 특성보다는 API 제공하는 상호 작용 표현에 집중할 있습니다.

그렇다고 항상 올바른 접근 방식을 취하는 것은 아닙니다. 함수 기반 대신 클래스 기반 뷰를 사용할 때와 마찬가지로 고려해야 유사한 절충점이 있습니다. 집합을 사용하는 것은 보기를 개별적으로 작성하는 것보다 명백합니다.

튜토리얼의 7 부에서는 클라이언트 라이브러리 또는 명령 도구를 사용하여 API 스키마를 추가하고 API 상호 작용하는 방법을 살펴볼 것이다.


Tutorial 7: Schemas & client libraries

스키마는 사용 가능한 API 엔드 포인트, 해당 URL 지원하는 조작을 설명하는 기계 가독성 문서입니다.

스키마는 자동 생성 문서에 유용한 도구일 있으며 API 상호 작용할 있는 동적 클라이언트 라이브러리를 구동하는 사용될 수도 있습니다.

Core API

스키마 지원을 제공하기 위해 REST 프레임 워크는 Core API 사용한다.

핵심 API API 설명하기 위한 문서 사양입니다. 사용 가능한 엔드 포인트 API 노출할 있는 가능한 상호 작용의 내부 표현 형식을 제공하는 사용됩니다. 서버 또는 클라이언트 하나를 사용할 있습니다.

서버 측에서 사용할 경우 Core API 사용하면 API 다양한 스키마 또는 하이퍼 미디어 형식으로의 렌더링을 지원할 있습니다.

클라이언트 측에서 사용되는 경우 Core API 지원되는 스키마 또는 하이퍼 미디어 형식을 제공하는 API 상호 작용할 있는 동적으로 구동되는 클라이언트 라이브러리를 허용합니다.

Adding a schema

REST 프레임 워크는 명시적으로 정의된 스키마 또는 자동 생성된 스키마를 지원합니다. 우리는 세트와 라우터를 사용하기 때문에 단순히 자동 스키마 생성을 사용할 있습니다.

API 스키마를 포함하려면 coreapi python 패키지를 설치해야 합니다.

$ pip install coreapi

이제 URL 구성에 자동 생성된 스키마 보기를 포함시켜 API 대한 스키마를 포함할 있습니다.

from rest_framework.schemas import get_schema_view

 

schema_view = get_schema_view(title='Pastebin API')

 

urlpatterns = [

    url('^schema/$', schema_view),

    ...

]

브라우저에서 API 루트 엔드 포인트를 방문하면 corejson 표현이 옵션으로 사용 가능해집니다.

Schema format

Accept 헤더에 원하는 내용 유형을 지정하여 명령 줄에서 스키마를 요청할 수도 있습니다.

$ http http://127.0.0.1:8000/schema/ Accept:application/coreapi+json

HTTP/1.0 200 OK

Allow: GET, HEAD, OPTIONS

Content-Type: application/coreapi+json

 

{

    "_meta": {

        "title": "Pastebin API"

    },

    "_type": "document",

    ...

기본 출력 스타일은 Core JSON 인코딩을 사용하는 것입니다.

Open API (이전 Swagger) 같은 다른 스키마 형식도 지원됩니다.

Using a command line client

이제 Google API 스키마 끝점을 노출하므로 동적 클라이언트 라이브러리를 사용하여 API 상호 작용할 있습니다. 이를 증명하기 위해 Core API 명령 클라이언트를 사용 해보자.

커맨드 라인 클라이언트는 coreapi-cli 패키지로 제공됩니다:

$ pip install coreapi-cli

이제 명령 줄에서 사용할 있는지 확인하십시오 ...

$ coreapi

Usage: coreapi [OPTIONS] COMMAND [ARGS]...

 

  Command line client for interacting with CoreAPI services.

 

  Visit http://www.coreapi.org for more information.

 

Options:

  --version  Display the package version number.

  --help     Show this message and exit.

 

Commands:

...

먼저 커맨드 라인 클라이언트를 사용하여 API 스키마를 로드합니다.

$ coreapi get http://127.0.0.1:8000/schema/

<Pastebin API "http://127.0.0.1:8000/schema/">

    snippets: {

        highlight(id)

        list()

        read(id)

    }

    users: {

        list()

        read(id)

    }

아직 인증되지 않았으므로 API 대한 사용 권한을 설정한 방법에 따라 읽기 전용 끝점만 있습니다.

명령 클라이언트를 사용하여 기존 스니펫을 나열해 봅니다.

$ coreapi action snippets list

[

    {

        "url": "http://127.0.0.1:8000/snippets/1/",

        "id": 1,

        "highlight": "http://127.0.0.1:8000/snippets/1/highlight/",

        "owner": "lucy",

        "title": "Example",

        "code": "print('hello, world!')",

        "linenos": true,

        "language": "python",

        "style": "friendly"

    },

    ...

일부 API 엔드 포인트에는 명명된 매개 변수가 필요합니다. 예를 들어 특정 스니펫의 강조 표시 HTML 되돌리려면 ID 제공해야 합니다.

$ coreapi action snippets highlight --param id=1

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

 

<html>

<head>

  <title>Example</title>

  ...

Authenticating our client

스니펫을 생성, 편집 삭제하려면 올바른 사용자로 인증해야 합니다. 경우 기본 인증만 사용합니다.

아래의 <username> <password> 실제 사용자 이름과 암호로 바꾸십시오.

$ coreapi credentials add 127.0.0.1 <username>:<password> --auth basic

Added credentials

127.0.0.1 "Basic <...>"

이제 스키마를 다시 가져 오면 사용 가능한 상호 작용의 전체 세트를 있습니다.

$ coreapi reload

Pastebin API "http://127.0.0.1:8000/schema/">

    snippets: {

        create(code, [title], [linenos], [language], [style])

        delete(id)

        highlight(id)

        list()

        partial_update(id, [title], [code], [linenos], [language], [style])

        read(id)

        update(id, code, [title], [linenos], [language], [style])

    }

    users: {

        list()

        read(id)

    }

우리는 이제 끝점과 상호 작용할 있습니다. 예를 들어 스니펫을 만들려면 다음과 같이 하십시오.

$ coreapi action snippets create --param title="Example" --param code="print('hello, world')"

{

    "url": "http://127.0.0.1:8000/snippets/7/",

    "id": 7,

    "highlight": "http://127.0.0.1:8000/snippets/7/highlight/",

    "owner": "lucy",

    "title": "Example",

    "code": "print('hello, world')",

    "linenos": false,

    "language": "python",

    "style": "friendly"

}

그리고 스니펫을 삭제하려면 다음과 같이 하십시오.

$ coreapi action snippets delete --param id=7

개발자는 명령 클라이언트뿐만 아니라 클라이언트 라이브러리를 사용하여 API 상호 작용할 수도 있습니다. Python 클라이언트 라이브러리가 이들 번째로 사용 가능하며 Javascript 클라이언트 라이브러리가 출시될 예정입니다.

스키마 생성을 사용자 정의하고 핵심 API 클라이언트 라이브러리를 사용하는 방법에 대한 자세한 내용은 전체 설명서를 참조하십시오.

Reviewing our work

믿을 없을 정도로 적은 양의 코드로 완벽한 브라우징이 가능하고 스키마 중심의 클라이언트 라이브러리가 포함된 완벽한 pastebin API 제공되며 인증, 객체 단위 권한 여러 렌더러 형식이 제공됩니다.

우리는 디자인 프로세스의 단계를 거쳤으며, 커스터마이징이 필요한 경우 점차적으로 표준 Django 뷰를 사용하여 작업을 수행 있는 방법을 살펴 보았습니다.

GitHub에서 마지막 튜토리얼 코드를 검토하거나 샌드 박스에서 실제 예제를 시험해 있다.

Onwards and upwards

자습서가 끝났습니다. REST 프레임 워크 프로젝트에 많이 참여하려면 다음과 같이 시작할 있는 가지 위치가 있습니다.

·         문제를 검토하고 제출하고 요청을 함으로써 GitHub 기고하십시오.

·         Join the REST framework discussion group, and help build the community.

·         Follow the author on Twitter and say hi.

이제 멋진 것들을 만드십시오.

'Development > Django' 카테고리의 다른 글

[Django REST framework]Quickstart  (0) 2017.03.09
[django] 개발 과정.  (0) 2017.02.07