1 - Serialization


チュートリアル1:シーケンス化
紹介する
このチュートリアルでは、簡単な収録コードのハイライト表示を作成するWeb APIについて説明します.プロセス全体では、REST framewoekを構成する各コンポーネントについて説明し、各コンポーネントがどのように一緒に動作しているかを全面的に理解します.
このチュートリアルはかなり深いので、始める前にクッキーと一番好きなビールを用意しなければなりません.すばやく参照したい場合は、クイック入門ドキュメントを使用する必要があります.
注:このチュートリアルのコードは、Githubのtomchristie/rest-framework-tutorialリポジトリで入手できます.完全なインプリメンテーションは、オンラインで砂箱バージョンとしてテストすることもできます.
新しい環境を構築する
他のことをする前にvirtualenvで新しい仮想環境を作成します.これにより、私たちのパッケージ構成が、私たちが処理している他のプロジェクトと良好に隔離されていることが保証されます.
virtualenv env
source env/bin/activate

virtualenv環境では、必要なパッケージをインストールできます.
pip install django
pip install djangorestframework
pip install pygments  #       

注意:virtualenv環境を随時終了するには、deactivateを入力するだけです.詳細については、virtualenvドキュメントを参照してください.
スタート
では、コードを書き始めます.まず、新しいプロジェクトを作成しましょう.
cd ~
django-admin.py startproject tutorial
cd tutorial

上記の手順を完了すると、簡単なWeb APIを作成するためのアプリケーションを作成できます.
python manage.py startapp snippets

新規snippetsアプリケーションとrest_frameworkアプリケーションをINSTALLED_APPSに追加する必要があります.編集tutorial/settings.pyファイル:
INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

はい、私たちの準備は終わりました.
モデルの作成
このチュートリアルの目的を達成するために、コードクリップを格納するための簡単なSnippetモデルの作成を開始します.そして編集を続けますsnippets/models.pyファイル.注意:良いプログラミング方法には、コメントの追加が含まれます.このチュートリアル・コードのリポジトリ・バージョンでは見つけることができますが、コード自体に焦点を当てるために無視します.
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

#      pygments               
LEXERS = [item for item in get_all_lexers() if item[1]]
#      pygments          
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
#      pygments             
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


# Create your models here.
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',)

また、コードセグメントモデルの初期移行(initial migration)を作成し、データベースを初めて同期する必要があります.
python manage.py makemigrations snippets
python manage.py migrate

シーケンス化クラスの作成
Web APIの使用を開始する最初のことは、コードフラグメントインスタンスを表示形式(例えばjson)にシーケンス化および逆シーケンス化する方法を提供することである.Django formsによく似たシーケンス化器(serializers)を宣言することで実現できる.snippetsというディレクトリの下に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):
        """
                          Snippet   。
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
                          Snippet   。
        """
        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

シーケンサクラスの第1部では、シーケンサ/逆シーケンサを取得するフィールドを定義します.create()およびupdate()メソッドは、呼び出しserializer.save()のときに完全なインスタンスを作成および変更する方法を定義します.
シーケンサクラスはDjangoFormクラスと非常に類似しており、各種フィールドに類似の検証フラグ、例えばrequiredmax_lengthおよびdefaultが含まれている.
フィールドフラグは、HTMLをレンダリングするなど、serializerが場合によってはどのように表示されるかを制御することもできます.上の{'base_template': 'textarea.html'}フラグはDjangoFormクラスで使用widget=widgets.Textareaと同等です.これは、ブラウズ可能なAPIの表示方法を制御するのに特に役立ちます.このチュートリアルの後で説明します.
実際にはModelSerializerクラスを使用することで時間を節約できますが、後で見ますが、明確に定義したserializerを使用します.
Serializersの使用
私たちがさらに理解する前に、私たちの新しいSerializerクラスを熟知します.Django shellに入りましょう.
python manage.py shell

では、いくつかのモジュールをインポートし、いくつかのコードクリップを作成して処理し始めました.
(env) fang@ubuntu:~/django_rest_framework/tutorial$ python manage.py shell
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 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"
'
) >>> snippet.save() >>> >>> snippet = Snippet(code='print "hello, world"
'
) >>> snippet.save() >>>

いくつかの使用可能なフラグメントインスタンスがあります.シーケンス化されたインスタンスの1つを見てみましょう.
>>> serializer = SnippetSerializer(snippet)
>>> serializer.data
{'language': 'python', 'code': 'print "hello, world"
'
, 'style': 'friendly', 'id': 2, 'linenos': False, 'title': ''} >>>

このとき,モデルインスタンスをPythonオリジナルデータ型に変換する.シーケンス化プロセスを完了するには、データをjsonにレンダリングします.
>>> content = JSONRenderer().render(serializer.data)
>>> content
b'{"id":2,"title":"","code":"print \\"hello, world\\"\
","linenos":false,"language":"python","style":"friendly"}'
>>>

逆シーケンス化は似ています.まず、Pythonのオリジナルデータ型としてストリームを解析します.
>>> from django.utils.six import BytesIO
>>> 
>>> stream = BytesIO(content)
>>> data = JSONParser().parse(stream)
>>> data
{'language': 'python', 'code': 'print "hello, world"
'
, 'style': 'friendly', 'id': 2, 'linenos': False, 'title': ''} >>>

...その後、これらのオリジナルデータ型を通常のオブジェクトインスタンスに復元します.
>>> serializer = SnippetSerializer(data=data)
>>> serializer.is_valid()
True
>>> serializer.validated_data
OrderedDict([('title', ''), ('code', 'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])
>>> serializer.save()
<Snippet: Snippet object (3)>
>>>

APIの動作形式とフォーム(forms)がどのように似ているかに注意してください.シーケンス化クラスを使用してビューを記述し始めると、類似性がより顕著になります.
モデルインスタンスの代わりにクエリーセットをシーケンス化することもできます.このため、シーケンス化パラメータにmany = Trueフラグを追加するだけです.
>>> serializer = SnippetSerializer(Snippet.objects.all(), many=True)
>>> serializer.data
[OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar"
'
), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print "hello, world"
'
), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])] >>>

ModelSerializersの使用
我々のSnippetSerializerクラスではSnippetモデルに含まれる多くの情報が重複している.コードを簡潔に保つことができれば、もっといいです.
Djangoが提供したFormクラスとModelFormクラスのように、REST frameworkはSerializerクラスとModelSerializerクラスを含む.ModelSerializerクラスを使用してシーケンス化クラスを再構築してみましょう.snippets/serializers.pyファイルを再度開き、SnippetSerializerクラスを以下に置き換えます.
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')

シーケンス化プログラムの素晴らしいプロパティは、その構造を印刷することで、シーケンス化インスタンスのすべてのフィールドをチェックすることです.
>>> from snippets.serializers import SnippetSerializer
>>> serializer = SnippetSerializer()
>>> print(repr(serializer))
SnippetSerializer():
    id = IntegerField(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=[('abap', 'ABAP'), ('abnf', 'ABNF'), ('ada', 'Ada'), ..., ('zephir', 'Zephir')], default='python')
    style = ChoiceField(choices=[('abap', 'abap'), ('algol', 'algol'), ('algol_nu', 'algol_nu'), ..., ('xcode', 'xcode')], default='friendly')
>>> 

覚えておいてModelSerializerクラスは特に不思議なことはしません.シーケンス化器クラスを作成するショートカットにすぎません.
  • フィールドのセットを自動的に決定する.(クラス属性の再定義は不要)
  • デフォルトで簡単に実現されるcreate()およびupdate()メソッド.

  • 我々のSerializerを使用して、従来のDjangoビューを作成します.
    新しいSerializerクラスを使用してAPIビューを作成する方法を見てみましょう.現在、REST frameworkの他の機能は使用されていません.通常のDjangoビューのみを作成します.snippets/views.pyファイルを編集し、以下の内容を追加します.
    from django.http import HttpResponse, JsonResponse
    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
    

    我々のAPIのルートビューは、すべての既存のsnippetをリストするか、新しいsnippetを作成することをサポートします.
    @csrf_exempt
    def snippet_list(request):
        """
                snippet,        snippet。
        """
        if request.method == 'GET':
            snippets = Snippet.objects.all()
            serializer = SnippetSerializer(snippets, many=True)
            return JsonResponse(serializer.data, safe=False)
        
        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 frameworkビューは実際にはこれよりも実用的な動作ですが、私たちの目的を達成するのに十分です.
    また、単一のsnippetに対応するビューが必要であり、snippetの取得、更新、または削除に使用できます.
    @csrf_exempt
    def snippet_detail(request, pk):
        """
          ,          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[0-9]+)/$', views.snippet_detail),
    ]
    

    また、snippetsアプリケーションのURLsを含むroot urlconfをtutorial/urls.pyファイルに接続する必要があります.
    from django.conf.urls import url, include
    
    urlpatterns = [
        url(r'^', include('snippets.urls')),
    ]
    

    注目すべきは、まだいくつかのエッジケースを処理していないことです.フォーマットが間違っているjsonを送信したり、ビューで処理しない方法で要求を発行したりすると、最終的に500「server error」の応答が得られます.とにかく、今私たちはしばらくそうします.
    Web APIでの最初のアクセスをテスト
    次に、snippetsのサーバを起動します.
    終了...
    quit()
    

    ...Djangoの開発サーバを起動します.
    (env) fang@ubuntu:~/django_rest_framework/tutorial$ python manage.py runserver
    Performing system checks...
    
    System check identified no issues (0 silenced).
    June 13, 2018 - 10:17:11
    Django version 2.0.6, using settings 'tutorial.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
    

    別のターミナルウィンドウを開き、サーバをテストできます.
    curlまたはhttpieを使用して、APIをテストすることができます.HttpieはPythonで作成されたユーザーに優しいhttpクライアントです.インストールしましょう.
    pipを使用してhttpieをインストールできます.
    pip install httpie
    

    最後に、すべてのsnippetのリストを得ることができます.
    (env) fang@ubuntu:~/django_rest_framework/tutorial$ http http://127.0.0.1:8000/snippets/
    HTTP/1.1 200 OK
    Content-Length: 352
    Content-Type: application/json
    Date: Wed, 13 Jun 2018 10:23:58 GMT
    Server: WSGIServer/0.2 CPython/3.5.2
    X-Frame-Options: SAMEORIGIN
    
    [
        {
            "code": "foo = \"bar\"
    "
    , "id": 1, "language": "python", "linenos": false, "style": "friendly", "title": "" }, { "code": "print \"hello, world\"
    "
    , "id": 2, "language": "python", "linenos": false, "style": "friendly", "title": "" }, { "code": "print \"hello, world\"", "id": 3, "language": "python", "linenos": false, "style": "friendly", "title": "" } ]

    あるいは、idを参照することで、特定のsnippetを取得することができます.
    (env) fang@ubuntu:~/django_rest_framework/tutorial$ http http://127.0.0.1:8000/snippets/2/
    HTTP/1.1 200 OK
    Content-Length: 119
    Content-Type: application/json
    Date: Wed, 13 Jun 2018 10:25:50 GMT
    Server: WSGIServer/0.2 CPython/3.5.2
    X-Frame-Options: SAMEORIGIN
    
    {
        "code": "print \"hello, world\"
    "
    , "id": 2, "language": "python", "linenos": false, "style": "friendly", "title": "" }

    同様に、WebブラウザでこれらのURLにアクセスすることで、同じjsonを表示することができます.
    今どこにいるの?
    これまで、DjangoのForms APIと非常に似たシーケンス化APIと、いくつかの従来のDjangoビューを持っていました.
    サービスjson応答に加えて、APIビューでは現在特別なことは行われておらず、クリーンアップしたいエラー処理のエッジもありますが、通常動作しているWeb APIです.
    このチュートリアルの第2部では、これらのことを改善する方法について説明します.