DjangoでAPIを実装する方法(Django REST framework)の続きです。
以前作成した以下のモジュールに「Django REST framework」を組み込みます。
DjangoでListViewを用いて検索画面を実装する方法
目次
条件
- Django 2.1.7
- Python 3.7.0
- djangorestframework 3.9.2
実装
settings.py
INSTALLED_APPSに「rest_framework」を追加します。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'search.apps.SearchConfig', "bootstrap4", 'rest_framework', # 追加 ]
APIのページネーションを設定するため、以下を追記します。
REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 10 }
urls.py
プロジェクトのurls.py
以下を追記します。
from rest_framework.schemas import get_schema_view schema_view = get_schema_view(title='ListSample API') urlpatterns = [ ・・・ path('api-auth/', include('rest_framework.urls')), # API認証 path('schema/', schema_view), # APIスキーマ ]
アプリケーションのurls.py
以下を追記します。
from django.urls import include from rest_framework.routers import DefaultRouter # Create a router and register our viewsets with it. router = DefaultRouter() router.register('posts', views.PostViewSet) router.register('users', views.UserViewSet) urlpatterns = [ ・・・ # APIのルート path('api/', include(router.urls)), ]
models.py
以下のようなモデルとします。
「related_name=’posts’,」を追記します。
from django.db import models from django.urls import reverse from django.contrib.auth.models import AbstractUser class CustomUser(AbstractUser): def __str__(self): return self.username + ":" + self.email class Post(models.Model): """投稿モデル""" class Meta: db_table = 'post' title = models.CharField(verbose_name='タイトル', max_length=255) text = models.CharField(verbose_name='内容', max_length=255, default='', blank=True) author = models.ForeignKey( 'search.CustomUser', on_delete=models.CASCADE, related_name='posts', # 追記 ) created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True) updated_at = models.DateTimeField(verbose_name='更新日時', auto_now=True) def __str__(self): return self.title + ',' + self.text @staticmethod def get_absolute_url(self): return reverse('search:index')
views.py
以下を追記します。
from search.models import Post from search.serializers import PostSerializer, UserSerializer from rest_framework.decorators import api_view from rest_framework.response import Response from search.models import CustomUser from rest_framework import permissions from search.permissions import IsOwnerOrReadOnly from rest_framework import renderers from rest_framework import viewsets from rest_framework.decorators import action class PostViewSet(viewsets.ModelViewSet): """ This viewset automatically provides `list`, `create`, `retrieve`, `update` and `destroy` actions. Additionally we also provide an extra `highlight` action. """ queryset = Post.objects.all() serializer_class = PostSerializer permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly,) @action(detail=True, renderer_classes=[renderers.StaticHTMLRenderer]) def highlight(self, request, *args, **kwargs): post = self.get_object() return Response(post.highlighted) def perform_create(self, serializer): serializer.save(owner=self.request.user) class UserViewSet(viewsets.ReadOnlyModelViewSet): """ This viewset automatically provides `list` and `detail` actions. """ queryset = CustomUser.objects.all() serializer_class = UserSerializer @api_view(['GET']) def api_root(request, format=None): return Response({ 'users': reverse('user-list', request=request, format=format), 'posts': reverse('post-list', request=request, format=format), })
serializers.py
アプリケーションのディレクトリに「serializers.py」を新規作成します。
APIのシリアライズおよびリレーションなどを記述します。
from rest_framework import serializers from search.models import Post from search.models import CustomUser class PostSerializer(serializers.HyperlinkedModelSerializer): author = serializers.ReadOnlyField(source='author.username') url = serializers.HyperlinkedRelatedField( view_name="post-detail", read_only=True, lookup_field='id' ) class Meta: model = Post fields = ('url', 'id', 'author', 'title', 'text', 'created_at', 'updated_at',) class UserSerializer(serializers.HyperlinkedModelSerializer): # posts = serializers.HyperlinkedIdentityField(many=True, view_name='post-detail', read_only=True) posts = PostSerializer(many=True, read_only=True) url = serializers.HyperlinkedRelatedField( view_name="user-detail", read_only=True, lookup_field='id' ) class Meta: model = CustomUser fields = ('url', 'id', 'username', 'email', 'posts')
permissions.py
アプリケーションのディレクトリに「permissions.py」を新規作成します。 API処理の許可について記述します。 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 post. return obj.owner == request.user
実行結果
APIルート
http://127.0.0.1:8000/api/
Post一覧
http://127.0.0.1:8000/api/posts/
ユーザ一覧
http://127.0.0.1:8000/api/users/
APIスキーマ
http://127.0.0.1:8000/schema/
通常の画面
APIに加えて、通常の画面も使用できます。
サンプルソース
GitHubに当該記事のサンプルソースを公開しています。
参考
Django REST framework:Nested relationships
https://www.django-rest-framework.org/api-guide/relations/#nested-relationships