0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

drf-spectacular を使って django の API の Swagger UI を作成

Posted at

やりたいこと

django の API の Swagger UI を作成する。
ここでは drf-spectacular を使用する。

drf-spectacular

Swagger UI の画面例

  • /api/items の POST メソッドでオブジェクトを作成する。
    items_create_01.png

  • try it をクリックすると、パラメータを入力できるようになる。
    items_create_02.png

  • execute をクリックすると、API へのリクエスト結果が出力される。
    items_create_03.png

インストール

$ pip install drf-spectacular

django のプログラム例

ファイル構成

drf1
├── drf1
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── items
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   └ ...
│   ├── models.py
│   ├── serializers.py
│   ├── services.py
│   ├── tests.py
│   └── views.py
└── manage.py

django のアプリケーションの作成

items アプリケーションを作成し、items の API を実装する。

$ python manage.py startapp items

API ロジック (services.py)

items/services.py
from items.models import Item


def create(name):
    item = Item(
        name=name,
    )
    item.save()
    return item


def list():
    items = Item.objects.filter(
        is_deleted=False
    ).order_by("id")
    return items


def get_by_id(id):
    item = Item.objects.get(
        id=id,
        is_deleted=False,
    )
    return item


def get_by_name(name):
    item = Item.objects.filter(
        name=name,
        is_deleted=False,
    )
    return item


def to_json(item):
    data = {
        'id': item.id,
        'name': item.name,
        'is_deleted': item.is_deleted,
    }
    return data


def to_json_list(items):
    data = [to_json(item) for item in items]
    return data

モデル (items.py)

items/models.py
from django.db import models


class Item(models.Model):
    name = models.CharField(max_length=32)
    is_deleted = models.BooleanField(default=False)

API 本体 (views.py)

items/views.py
from django.views.decorators.csrf import csrf_exempt
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import serializers
from rest_framework import status
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.views import APIView

from items.serializers import (
    ItemNameSerializer,
    ItemIdSerializer,
)

from items.services import (
    create,
    list,
    get_by_id,
    get_by_name,
    to_json,
    to_json_list,
)


class ApiViewSet(viewsets.GenericViewSet):
    def get_serializer_class(self):
        if self.action == 'api_create':
            return ItemNameSerializer
        elif self.action == 'api_get_by_id':
            return ItemIdSerializer
        elif self.action == 'api_get_by_name':
            return ItemNameSerializer
        else:
            return super().get_serializer_class()


    @csrf_exempt
    @action(detail=False, methods=['post'])
    def api_create(self, request):
        content = {
            'data': None,
        }

        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            name = serializer.validated_data.get('name')
            item = create(name)
            content['data'] = to_json(item)
            return Response(content, status=status.HTTP_201_CREATED)
        else:
            return Response(content, status=status.HTTP_400_BAD_REQUEST)


    @csrf_exempt
    @action(detail=False, methods=['get'])
    def api_list(self, request):
        content = {
            'data': None,
        }

        items = list()
        content['data'] = to_json_list(items)
        return Response(content)


    @csrf_exempt
    @action(detail=False, methods=['get'])
    def api_get_by_id(self, request, id=None):
        content = {
            'data': None,
        }

        item = get_by_id(id)
        if item:
            content['data'] = to_json(item)
            return Response(content)

        return Response(content)


    @csrf_exempt
    @extend_schema(
        parameters=[OpenApiParameter(name='name', type=str)],
    )
    @action(detail=False, methods=['get'])
    def api_get_by_name(self, request):
        content = {
            'data': None,
        }

        serializer = self.get_serializer(data=request.query_params)
        if serializer.is_valid(raise_exception=True):
            name = serializer.validated_data.get('name')
            items = get_by_name(name)
            content['data'] = to_json_list(items)
            return Response(content)

        return Response(content)

URL の設定 (urls.py)

from django.contrib import admin
from django.urls import path

from items.views import ApiViewSet


urlpatterns = [
    # path('admin/', admin.site.urls),

    path('api/items/', ApiViewSet.as_view({
        'post': 'api_create',
        'get': 'api_list',
    })),
    path('api/items/<int:id>/', ApiViewSet.as_view({'get': 'api_get_by_id'})),
    path('api/items/find_by_name/', ApiViewSet.as_view({'get': 'api_get_by_name'})),
]

drf-spectacular の設定

drf1/settings.py

settings.py
INSTALLED_APPS = [
    ...,

    # 追加で記述
    'rest_framework',
    'drf_spectacular',

    # items アプリケーション
    'items',
]
...

# drf-spectacular
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}

SPECTACULAR_SETTINGS = {
    'TITLE': 'test project APIs',
    'DESCRIPTION': 'description',
    'SERVE_INCLUDE_SCHEMA': False,
    'SWAGGER_UI_SETTINGS': {},
}

drf1/urls.py の変更

drf1/urls.py
# drf-spectacular の import
from drf_spectacular.views import (
    SpectacularAPIView,
    SpectacularRedocView,
    SpectacularSwaggerView,
)

# URLパターン Swagger UI のパスを追加
urlpatterns = [
    path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
    ...

Swagger UI に API のパラメータを認識させる方法

serializer を使うことで Swagger UI に API のパラメータを認識させることができる。

ここでは、serializer として name と id 用の 2 つの serializer を定義している。

items/serializers.py
class ItemNameSerializer(serializers.Serializer):
    name = serializers.CharField(help_text='name', max_length=32)

class ItemIdSerializer(serializers.Serializer):
    id = serializers.IntegerField(help_text='id')

views.py の get_serializer_class() で各 API でどの serializer を使うかを指定する。

items/views.py
def get_serializer_class(self):
    if self.action == 'api_create':
        return ItemNameSerializer
    elif self.action == 'api_get_by_id':
        return ItemIdSerializer
    elif self.action == 'api_get_by_name':
        return ItemNameSerializer
    else:
        return super().get_serializer_class()

各 API では get_serializer() を呼び出して serializer を使用する。

  • api_create() では POST パラメータから name を取得する。
items/views.py
serializer = self.get_serializer(data=request.data)
  • api_get_by_name() ではクエリパラメータから name を取得する
serializer = self.get_serializer(data=request.query_params)
  • api_get_by_id() ではパスから id を取得する。
drf1/urls.py
path('api/items/<int:id>/', ApiViewSet.as_view({'get': 'api_get_by_id'})),

サーバ起動

$ python manage.py runserver 0.0.0.0:8000

Swagger UI の表示

drf1/urls.py に 定義した SpectacularSwaggerView のパスにブラウザでアクセスすると、Swagger UI が表示される。

http://{IP address}:8000/api/schema/swagger-ui/
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?