Django REST framework作成RESTFUL API

12526 ワード

  • RESTful仕様に適合するAPIを自動的に生成する
  • はOPTION、HEAD、POST、GET、PATCH、PUT、DELETE
  • をサポートする.
  • Content-Typeに従って動的に返されるデータ型(例えばtext、json)
  • .
  • browserableのインタラクティブページを生成する(APIのために非常に友好的なブラウザページを自動的に生成する)
  • 非常に微細度の権限管理(fieldレベルまで微細度可能)
  • 見取り図
    インストール
    $ pip install djangorestframework
    $ pip install markdown
    

    概要
    Django Rest frameworkの流れは多分こうです
  • Models
  • の構築
  • Serialiersによりデータベースから取り出すデータParseをAPIとするデータ(クライアントへの返信にもブラウザ表示にも使用可能)
  • .
  • ViewSetは1つのviewsの集合であり、クライアントの要求(GET、POSTなど)に従って、Serialiersが処理したデータを返す
  • 権限Premissionsもこのステップで
  • を処理する.
  • ViewSetはRoutersに登録できます.登録するとApi Rootページに
  • が表示されます.
  • urlsにViewSet生成のviewを登録し、傍受を指定するurl
  • 詳しく知りたい方は公式文書を見に足を運んでください.
    準備作業&Models
    小さなプロジェクトの練習手を書きましょう
  • まずmanage.py startproject restで1つのプロジェクト
  • を生成する.
  • は、manage.py createsuperuserを使用してユーザを作成する(後述の権限管理で使用される)
  • .
  • データベースmanage.py migrate
  • の初期化
    そしてもちろんmodelsを書き、rest_を示すためにframeworkの強みはmodelsにカスタムfieldを定義しました
    # myproject/myapp/models.py
    
    
    #! /usr/bin/env python
    # -*- coding: utf-8
    from __future__ import unicode_literals, absolute_import
    import cPickle as pickle
    
    from django.db import models
    from django.contrib.auth.models import User
    
    
    class SerializedField(models.TextField):
    
        """    
          pickle       Python   
        """
        __metaclass__ = models.SubfieldBase  #       metaclass      to_python
    
        def validate(self, val):
            raise isinstance(val, basestring)
    
        def to_python(self, val):
            """          ,    python   """
            if val and isinstance(val, unicode):
                return pickle.loads(val.encode('utf-8'))
    
            return val
    
        def get_prep_value(self, val):
            """  python object      """
            return pickle.dumps(val)
    
    
    class MyModel(models.Model):
    
        created_at = models.DateTimeField(auto_now_add=True)
        #            
        owner = models.ForeignKey(User, related_name='mymodels')
        field = models.CharField(max_length=100)
        options = SerializedField(max_length=1000, default={})
    

    Serializers
    Modelsを定義したら、DjangoのFormに相当するSerializersを書くことができます.
    # myproject/myapp/serializers.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8
    from __future__ import unicode_literals, absolute_import
    import json
    
    from django.contrib.auth.models import User
    from rest_framework import serializers
    
    from ..models import MyModel
    from .fields import MyCustField
    
    
    class MyCustField(serializers.CharField):
        """  Model               Serializer Field"""
    
        def to_representation(self, obj):
            """   Model       parse   Api"""
            return obj
    
        def to_internal_value(self, data):
            """        json    parse   Model"""
            return json.loads(data.encode('utf-8'))
    
    
    class UserSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = User  #       Model
            fields = ('id', 'username', 'mymodels')  #       fields
    
        #          MyModel          ,    urls    name   
        #                 
        mymodels = serializers.HyperlinkedRelatedField(
            many=True, queryset=MyModel.objects.all(),
            view_name='model-detail'
        )
    
    
    class MySerializer(serializers.ModelSerializer):
    
        options = MyCustField(
            max_length=1000, style={'base_template': 'textarea.html'},
        )
    
        class Meta:
            model = MyModel
            fields = ('id', 'owner', 'field', 'options')
            read_only_fields = ('owner',)  #       field
    
        def create(self, validated_data):
            """   POST   """
            #          model    owner
            validated_data['owner'] = self.context['request'].user
            return MyModel.objects.create(**validated_data)
    
        def update(self, instance, validated_data):
            """   PUT   """
            instance.field = validated_data.get('field', instance.field)
            instance.save()
            return instance
    

    ViewSet
    Serializersを定義すればviewsetを書くことができます
    実はviewsetはかえって最も簡単な部分で、rest_frameworkオリジナルは4種類のViewSetを提供しています
  • ViewSet
  • GenericViewSet
  • GenericAPIView
  • に継承する.
  • ModelViewSet
  • 自体は6つの方法
  • を提供している.
  • list
  • create
  • retrieve
  • update
  • partial_update
  • destroy

  • ReadOnlyModelViewSet

  • 私はModelViewSetを使って、Premissionsで権限を管理するのが好きです.
    # myproject/myapp/views.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8
    from __future__ import unicode_literals, absolute_import
    
    from django.contrib.auth.models import User
    from rest_framework import permissions, viewsets, renderers
    from rest_framework.decorators import (
        permission_classes, detail_route
    )
    from rest_framework.response import Response
    
    from .serializers import MySerializer, UserSerializer
    from .models import MyModel
    
    
    class UserViewSet(viewsets.ModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserSerializer
        #     ,      
        permission_classes = (permissions.IsAuthenticated,)
    
    
    class ModelViewSet(viewsets.ModelViewSet):
        queryset = MyModel.objects.all()
        serializer_class = MySerializer
        permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
    
        @detail_route(renderer_classes=[renderers.StaticHTMLRenderer])
        def plaintext(self, request, *args, **kwargs):
            """    Api   """
            model = self.get_object()
            return Response(repr(model))
    

    ModelViewSetでメソッドplaintext,rest_をカスタマイズしましたframeworkでは、カスタムviewsetメソッドに2つの装飾器が用意されています.
  • list_route
  • detail_route

  • 違いはlist_routeのパラメータがpk(対応リスト)を含まないことであり、detail_routepk(対応retrieve)を含むことである.
    コードを見ればわかる
    @list_route(methods=['post', 'delete'])
    def custom_handler(self, request):
        pass
    
    
    @detail_route(methods=['get'])
    def custom_handler(self, request, pk=None):
        pass
    

    Filters
    前にserializersとviewsetに基づいて、データインタフェースと展示をよく提供することができます.しかし、urlパラメータを使用してデータを並べ替えたりフィルタリングしたりする必要がある場合があります.そのため、rest-framworkはfiltersを提供してこのニーズを満たしています.
    グローバルフィルタ
    settingsでグローバルに適用するfilterを指定できます.
    REST_FRAMEWORK = {
        'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',)
    }
    

    viewsetのfilter
    viewsetにfilterをそれぞれ指定することもできます.viewsetを定義するときにfilter_backendというクラス変数を定義します.
    class UserListView(generics.ListAPIView):
        queryset = User.objects.all()
        serializer = UserSerializer
        filter_backends = (filters.DjangoFilterBackend,)
    

    デフォルトのフィルタ
    rest-frameworkはいくつかのオリジナルのfilterを提供しています.
  • SearchFilter
  • filter_backends = (filters.SearchFilter,)
    search_fields = ('username', 'email')  #       
    

    要求http://example.com/api/users?search=russell.
  • OrderingFilter
  • filter_backends = (filters.OrderingFilter,)
    ordering_fields = ('username', 'email')
    

    要求http://example.com/api/users?ordering=account,-username.
    カスタムフィルタ
    カスタムfilterは、filter_queryset(self, request, queryset, view)メソッドを定義し、querysetを返すだけで簡単です.
    私が書いた例を直接貼ります.
    class NodenameFilter(filters.BaseFilterBackend):
    
        """   nodename    
          [nodename]: NeiWang
        """
    
        def filter_queryset(self, request, queryset, view):
            nodename = request.QUERY_PARAMS.get('nodename')
            if nodename:
                return queryset.filter(nodename=nodename)
            else:
                return queryset
    

    パラメータマッチングに誤りがあり、異常を投げ出したい場合は、APIErrorをカスタマイズして、例を挙げます.
    from rest_framework.exceptions import APIException
    
    
    class FilterError(APIException):
        status_code = 406
        default_detail = 'Query arguments error!'
    

    そしてviewsetにraise FilterErrorを直接投げ出せばいいです.
    Premissions
    名前の通り権限管理であり、ViewSetに権限を設定するために使用されます.premissionsを使用すると、異なるレベルの権限を簡単に設定できます.
  • グローバル権限制御
  • ViewSetの権限制御
  • Methodの権限
  • Objectの権限
  • premissionによってブロックされたリクエストには、次のような結果が返されます.
  • ユーザーがログインしているがpremissionsによって制限されている場合、HTTP 403 Forbidden
  • に戻る.
  • ユーザーがログインしていない場合、premissionsによって制限されるとHTTP 401 Unauthorized
  • に戻る.
    デフォルトの権限
    rest_frameworkでは7つの権限が提供されています
  • AllowAny#無制限
  • IsAuthenticated#ログインユーザ
  • IsAdminUser#Adminユーザー
  • IsAuthenticatedOrReadOnly#非登録ユーザ読取り専用
  • DjangoModelPermissions#以下はDjangoによるModelPremissions
  • DjangoModelPermissionsOrAnonReadOnly
  • DjangoObjectPermissions

  • グローバル権限制御
    settings.pyでグローバルデフォルト権限を設定できます
    # settings.py
    
    REST_FRAMEWORK = {
        'DEFAULT_PERMISSION_CLASSES': (
            'rest_framework.permissions.AllowAny',
        ),
    }
    

    ViewSetの権限permission_classesのクラスプロパティを設定してviewsetに権限を設定できます.restframeworkはメタグループ内の各premissionをチェックし、すべて通過する必要があります.
    class UserViewSet(viewsets.ReadOnlyModelViewSet):
        queryset = User.objects.all()
        serializer_class = UserSerializer
        #     ,     
        permission_classes = (permissions.IsAuthenticated,)
    

    カスタム権限
    Premissionsは簡単にカスタマイズできます.例えば、owner編集のみを許可する権限を自分で書きました.
    # myproject/myapp/premissions.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8
    from __future__ import unicode_literals, absolute_import
    
    from rest_framework import permissions
    
    
    class IsOwnerOrReadOnly(permissions.BasePermission):
    
        def has_permission(self, request, view):
            """            """
            if request.method in permissions.SAFE_METHODS:
                return True
    
        def has_object_permission(self, request, view, obj):
            """            ,   True     """
            #         
            if request.method in permissions.SAFE_METHODS:
                return True
    
            #                owner
            return obj.owner == request.user
    

    urls & routers
    # myproject/myapp/urls.py
    
    #! /usr/bin/env python
    # -*- coding: utf-8
    from __future__ import unicode_literals, absolute_import
    
    from django.conf.urls import url, patterns, include
    from rest_framework.routers import DefaultRouter
    
    from . import views
    
    
    # as_view      view
    #           `{Http Method: View Method}`
    user_detail = views.UserViewSet.as_view({'get': 'retrieve'})
    user_list = views.UserViewSet.as_view({'get': 'list', 'post': 'create'})
    
    # plaintext         ,          
    modal_plain = views.ModelViewSet.as_view({'get': 'plaintext'})
    model_detail = views.ModelViewSet.as_view({'get': 'retrieve', 'post': 'create'})
    model_list = views.ModelViewSet.as_view({'get': 'list', 'post': 'create'})
    
    # router           Api Root   
    router = DefaultRouter()
    router.register(r'models', views.ModelViewSet)
    router.register(r'users', views.UserViewSet)
    
    
    #       views     urls  
    urlpatterns = patterns(
        '',
        url(r'^', include(router.urls)),  # Api Root
        url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
        url(r'^models/(?P[0-9]+)/$', model_detail, name='model-detail'),
        url(r'^models/(?P[0-9]+)/plain/$', modal_plain, name='model-plain'),
        url(r'^models/$', model_list, name='model-list'),
        url(r'^users/$', user_list, name='user-list'),
        url(r'^users/(?P[0-9]+)/$', user_detail, name='user-detail'),
    )
    

    急いで、これらを紹介して、後で暇があればDjangoでJWTを身分証明書として使うことを紹介します.次はいくつかの効果図です.
  • Api Root
  • Users