Django REST framework作成RESTFUL API
12526 ワード
Content-Type
に従って動的に返されるデータ型(例えばtext、json)インストール
$ pip install djangorestframework
$ pip install markdown
概要
Django Rest frameworkの流れは多分こうです
準備作業&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
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_route
がpk
(対応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を提供しています.
filter_backends = (filters.SearchFilter,)
search_fields = ('username', 'email') #
要求
http://example.com/api/users?search=russell
.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を使用すると、異なるレベルの権限を簡単に設定できます.
HTTP 403 Forbidden
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を身分証明書として使うことを紹介します.次はいくつかの効果図です.