[DRF] drf-spectacular API 문서 자동화 라이브러리 정리
1. drf-spectacular 설치 방법
$ pip install drf-spectacular # pip을 사용하면 이렇게 설치
$ poetry add drf-spectacular # poetry를 사용하면 이렇게 설치
2. settings.py 설정
https://drf-spectacular.readthedocs.io/en/latest/settings.html#settings
- 기본 설정
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'drf_spectacular',
# ...
]
REST_FRAMEWORK = {
# YOUR SETTINGS drf의 schema 클래스를 drf-specacular의 AutoSchema로 교체해줍니다.
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
- 타이틀, 버전 및 설몆 설정
SPECTACULAR_SETTINGS = {
'TITLE': 'My API Service',
'DESCRIPTION': 'A detailed description of my API.',
'VERSION': '1.0.0',
}
- 스키마 노출 설정
- OAS3 Meta정보 API를 비노출 처리한다.
SPECTACULAR_SETTINGS = {
'SERVE_INCLUDE_SCHEMA': False,
}
- 웹 인터페이스에서 파일 업로드 기능 제공 설정
SPECTACULAR_SETTINGS = {
'COMPONENT_SPLIT_REQUEST': True,
}
- API 문서에 표시될 연락처 정보를 설정
- 일반적으로 개발자나 팀의 연락처 정보를 입력한다.
SPECTACULAR_SETTINGS = {
'CONTACT': {
'name': 'John Doe',
'email': 'johndoe@example.com',
'url': 'https://www.example.com',
}
}
- Swagger UI에 대한 설정을 관리하는 옵션
- 이 옵션을 통해 Swagger UI의 테마, URL 등을 설정할 수 있다.
- SWAGGER_UI_SETTINGS 하위 옵션들은 Swagger-UI(JavaScript)에 반영되는 옵션들이기 때문에 카멜표기법에 기반하여 작성하여야 한다.
SPECTACULAR_SETTINGS = {
'SWAGGER_UI_SETTINGS': {
'deepLinking': True,
'theme': 'dark',
'validatorUrl': None,
}
}
# https://pypi.org/project/form-schema-generator/ 에서 참조함
'SWAGGER_UI_SETTINGS': {
# https://swagger.io/docs/open-source-tools/swagger-ui/usage/configuration/ <- 여기 들어가면 어떤 옵션들이 더 있는지 알수있습니다.
'dom_id': '#swagger-ui', # required(default)
'layout': 'BaseLayout', # required(default)
'deepLinking': True, # API를 클릭할때 마다 SwaggerUI의 url이 변경됩니다. (특정 API url 공유시 유용하기때문에 True설정을 사용합니다)
'persistAuthorization': True, # True 이면 SwaggerUI상 Authorize에 입력된 정보가 새로고침을 하더라도 초기화되지 않습니다.
'displayOperationId': True, # True이면 API의 urlId 값을 노출합니다. 대체로 DRF api name둘과 일치하기때문에 api를 찾을때 유용합니다.
'filter': True, # True 이면 Swagger UI에서 'Filter by Tag' 검색이 가능합니다
},
- API 문서에 표시될 라이선스 정보를 설정
SPECTACULAR_SETTINGS = {
'LICENSE': {
'name': 'MIT License',
'url': 'https://opensource.org/licenses/MIT',
# github의 LICENSE 주소도 가능
}
}
- Swagger UI의 정적 파일 경로를 설정하는 옵션
- Swagger UI의 정적 파일을 원하는 경로에 저장하거나 다른 서버에서 호스팅할 수 있다.
SPECTACULAR_SETTINGS = {
'SWAGGER_UI_DIST': '/static/swagger-ui/',
}
# https://pypi.org/project/form-schema-generator/ 에서 참조
SPECTACULAR_SETTINGS = {
# https://www.npmjs.com/package/swagger-ui-dist 해당 링크에서 최신버전을 확인후 취향에 따라 version을 수정해서 사용하세요.
'SWAGGER_UI_DIST': '//unpkg.com/swagger-ui-dist@3.38.0',
}
SPECTACULAR_SETTINGS = {
# .....
# drf-spectacular 라이브러리 version up이 없이도 자신의 원하는 swagger-ui의 version을 사용할수있다.
# swagger-ui version 정보는 여기서 확인 https://www.npmjs.com/package/swagger-ui
'SWAGGER_UI_DIST': '//unpkg.com/swagger-ui-dist@3.44.0',
'SWAGGER_UI_FAVICON_HREF': '//unpkg.com/swagger-ui-dist@3.44.0/favicon-32x32.png',
# ....
}
3. urls.py 설정
- 예전 자료를 보면 json, yaml과 관련된 path를 정의해 줘야 했다. 하지만, 최신 버전에서는 SpectacularAPIView만 정의해주면 된다.
# 이전 방식
from drf_spectacular.views import SpectacularJSONAPIView
from drf_spectacular.views import SpectacularYAMLAPIView
urlpatterns = [
path("docs/json/", SpectacularJSONAPIView.as_view(), name="schema-json"),
path("docs/yaml/", SpectacularYAMLAPIView.as_view(), name="swagger-yaml"),
...
]
# 최신 방식
from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
urlpatterns = [
# YOUR PATTERNS
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
# Optional UI:
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
]
4. 기본 사용 방법
1) @extend_schema
- 메소드 단위의 데코레이터 , 하나의 메소드(path)에 해당하는 문서화를 커스터마이징 할때 사용하고, 가장 핵심이 되는 데코레이터이다.
- 사용 가능한 파라메터는 아래 함수 정의를 참고할 수 있다.
def extend_schema(
operation_id: Optional[str] = None,
parameters: Optional[Sequence[Union[OpenApiParameter, _SerializerType]]] = None,
request: Any = empty,
responses: Any = empty,
auth: Optional[Sequence[str]] = None,
description: Optional[_StrOrPromise] = None,
summary: Optional[_StrOrPromise] = None,
deprecated: Optional[bool] = None,
tags: Optional[Sequence[str]] = None,
filters: Optional[bool] = None,
exclude: Optional[bool] = None,
operation: Optional[_SchemaType] = None,
methods: Optional[Sequence[str]] = None,
versions: Optional[Sequence[str]] = None,
examples: Optional[Sequence[OpenApiExample]] = None,
extensions: Optional[Dict[str, Any]] = None,
callbacks: Optional[Sequence[OpenApiCallback]] = None,
external_docs: Optional[Union[Dict[str, str], str]] = None,
) -> Callable[[F], F]:
"""
Decorator mainly for the "view" method kind. Partially or completely overrides
what would be otherwise generated by drf-spectacular.
:param operation_id: replaces the auto-generated operation_id. make sure there
are no naming collisions.
:param parameters: list of additional or replacement parameters added to the
auto-discovered fields.
:param responses: replaces the discovered Serializer. Takes a variety of
inputs that can be used individually or combined
- ``Serializer`` class
- ``Serializer`` instance (e.g. ``Serializer(many=True)`` for listings)
- basic types or instances of ``OpenApiTypes``
- :class:`.OpenApiResponse` for bundling any of the other choices together with
either a dedicated response description and/or examples.
- :class:`.PolymorphicProxySerializer` for signaling that
the operation may yield data from different serializers depending
on the circumstances.
- ``dict`` with status codes as keys and one of the above as values.
Additionally in this case, it is also possible to provide a raw schema dict
as value.
- ``dict`` with tuples (status_code, media_type) as keys and one of the above
as values. Additionally in this case, it is also possible to provide a raw
schema dict as value.
:param request: replaces the discovered ``Serializer``. Takes a variety of inputs
- ``Serializer`` class/instance
- basic types or instances of ``OpenApiTypes``
- :class:`.PolymorphicProxySerializer` for signaling that the operation
accepts a set of different types of objects.
- ``dict`` with media_type as keys and one of the above as values. Additionally, in
this case, it is also possible to provide a raw schema dict as value.
:param auth: replace discovered auth with explicit list of auth methods
:param description: replaces discovered doc strings
:param summary: an optional short summary of the description
:param deprecated: mark operation as deprecated
:param tags: override default list of tags
:param filters: ignore list detection and forcefully enable/disable filter discovery
:param exclude: set True to exclude operation from schema
:param operation: manually override what auto-discovery would generate. you must
provide a OpenAPI3-compliant dictionary that gets directly translated to YAML.
:param methods: scope extend_schema to specific methods. matches all by default.
:param versions: scope extend_schema to specific API version. matches all by default.
:param examples: attach request/response examples to the operation
:param extensions: specification extensions, e.g. ``x-badges``, ``x-code-samples``, etc.
:param callbacks: associate callbacks with this endpoint
:param external_docs: Link external documentation. Provide a dict with an "url" key and
optionally a "description" key. For convenience, if only a string is given it is
treated as the URL.
:return:
"""
# operation_id : 자동으로 설정되는 id 값, 대체로 수동할당하여 쓰진 않음
# parameters : 해당 path로 받기로 예상된 파라미터 값 (Serializer or OpenApiParameter 사용)
# request : 요청시 전달될 content의 형태
# responses : 응답시 전달될 content의 형태
# auth : 해당 method에 접근하기 위한 인증방법
# description: 해당 method 설명
# summary : 해당 method 요약
# deprecated : 해당 method 사용여부
# tags : 문서상 보여줄 묶음의 단위
# exclude : 문서에서 제외여부
# operation : ??? json -> yaml 하기위한 dictionary???
# methods : 요청 받을 Http method 목록
# versions : 문서화 할때 사용할 openAPI 버전
# examples : 요청/응답에 대한 예시
# ...methods...
- 사용 예시 :
from django.http import JsonResponse
from rest_framework.decorators import api_view
from drf_spectacular.utils import extend_schema
from .serializers import ExampleModelSerializer
# 함수형 뷰
@extend_schema(
methods=['GET'], # 이 데코레이터를 적용할 HTTP 메서드
summary='ExampleModel 리스트 조회', # API 요약 설명
description='ExampleModel의 리스트를 조회하는 API입니다.', # API 상세 설명
responses={200: ExampleModelSerializer(many=True)}, # 응답 스키마
)
@api_view(['GET'])
def example_model_list(request):
if request.method == 'GET':
# 데이터 조회 로직
data = {'message': 'ExampleModel 리스트 조회 성공'}
return JsonResponse(data, safe=False)
@extend_schema(
methods=['POST'], # 이 데코레이터를 적용할 HTTP 메서드
summary='ExampleModel 생성', # API 요약 설명
description='새로운 ExampleModel을 생성하는 API입니다.', # API 상세 설명
request=ExampleModelSerializer, # 요청 스키마
responses={201: ExampleModelSerializer}, # 응답 스키마
)
@api_view(['POST'])
def example_model_create(request):
if request.method == 'POST':
# 데이터 생성 로직
data = {'message': 'ExampleModel 생성 성공'}
return JsonResponse(data, safe=False, status=201)
2) @extend_schema_view
- 하나의 ViewSet에 속한 method들의 문서화를 커스터마이징 할 때 사용할 수 있고 APIView, ViewSet, 그리고 일반 View 클래스 등 다양한 종류의 뷰에서 사용이 가능하다.
- 만약, @extend_schema 와 동시에 쓰인다면 @extend_schema가 우선순위를 가진다.
- view_name은 기본적으로 list,retrieve,create,update,delete가 있고, @action을 통해 만든 커스텀 메소드도 메소드 명을 사용해 커스텀 할 수 있다.
def extend_schema_view(**kwargs) -> Callable[[F], F]:
"""
Convenience decorator for the "view" kind. Intended for annotating derived view methods that
are are not directly present in the view (usually methods like ``list`` or ``retrieve``).
Spares you from overriding methods like ``list``, only to perform a super call in the body
so that you have have something to attach :func:`@extend_schema <.extend_schema>` to.
This decorator also takes care of safely attaching annotations to derived view methods,
preventing leakage into unrelated views.
:param kwargs: method names as argument names and :func:`@extend_schema <.extend_schema>`
calls as values
"""
- APIView 클래스에서 extend_schema_view 적용
from django.http import JsonResponse
from rest_framework.views import APIView
from drf_spectacular.utils import extend_schema, extend_schema_view
from .serializers import ExampleModelSerializer
@extend_schema_view(
get=extend_schema(
summary='ExampleModel 조회',
description='ExampleModel의 세부 정보를 조회하는 API입니다.',
responses={200: ExampleModelSerializer},
),
post=extend_schema(
summary='ExampleModel 생성',
description='새로운 ExampleModel을 생성하는 API입니다.',
request=ExampleModelSerializer,
responses={201: ExampleModelSerializer},
)
)
class ExampleModelAPIView(APIView):
def get(self, request, *args, **kwargs):
# 데이터 조회 로직
return JsonResponse({'message': '조회 성공'}, status=200)
def post(self, request, *args, **kwargs):
# 데이터 생성 로직
return JsonResponse({'message': '생성 성공'}, status=201)
- Mixin과 Generic Views에 extend_schema_view 적용
from rest_framework import mixins, viewsets
from rest_framework.generics import GenericAPIView
from .models import ExampleModel
from .serializers import ExampleModelSerializer
from drf_spectacular.utils import extend_schema_view, extend_schema
# 리스트 생성 및 조회 API
@extend_schema_view(
get=extend_schema(description="ExampleModel 리스트를 조회합니다."),
post=extend_schema(description="새로운 ExampleModel을 생성합니다.")
)
class ExampleModelList(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
queryset = ExampleModel.objects.all()
serializer_class = ExampleModelSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# 단일 객체 조회, 업데이트 및 삭제 API
@extend_schema_view(
get=extend_schema(description="ExampleModel의 상세 정보를 조회합니다."),
put=extend_schema(description="ExampleModel을 업데이트합니다."),
delete=extend_schema(description="ExampleModel을 삭제합니다.")
)
class ExampleModelDetail(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericAPIView):
queryset = ExampleModel.objects.all()
serializer_class = ExampleModelSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
- ListCreateAPIView, RetrieveUpdateDestroyAPIView에 extend_schema_view 적용
from rest_framework.generics import (CreateAPIView, ListAPIView,
RetrieveAPIView, ListCreateAPIView,
RetrieveUpdateDestroyAPIView)
from .models import YourModel
from .serializers import YourModelSerializer
from drf_spectacular.utils import extend_schema_view, extend_schema
@extend_schema_view(
post=extend_schema(description="새로운 YourModel 인스턴스를 생성합니다."),
get=extend_schema(description="YourModel 인스턴스 목록을 가져옵니다.")
)
class YourModelListCreateAPIView(ListCreateAPIView):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
@extend_schema_view(
get=extend_schema(description="YourModel 인스턴스의 상세 정보를 가져옵니다."),
put=extend_schema(description="YourModel 인스턴스를 업데이트합니다."),
delete=extend_schema(description="YourModel 인스턴스를 삭제합니다.")
)
class YourModelRetrieveUpdateDestroyAPIView(RetrieveUpdateDestroyAPIView):
queryset = YourModel.objects.all()
serializer_class = YourModelSerializer
- ViewSet에 extend_schema_view 적용
from rest_framework import viewsets, mixins
from drf_spectacular.utils import extend_schema_view, extend_schema
from .models import User
from .serializers import UserSerializer
@extend_schema_view(
list=extend_schema(description="사용자 목록을 조회합니다."),
retrieve=extend_schema(description="특정 사용자의 상세 정보를 조회합니다.")
)
class UserViewSet(viewsets.GenericViewSet, mixins.ListModelMixin, mixins.RetrieveModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
from rest_framework.viewsets import ModelViewSet
from drf_spectacular.utils import extend_schema_view, extend_schema
from .models import ExampleModel
from .serializers import ExampleModelSerializer
# extend_schema_view를 사용해 개별 액션에 대한 스키마 정보 추가
@extend_schema_view(
list=extend_schema(description="모든 예시 항목의 리스트를 반환합니다."),
create=extend_schema(description="새로운 예시 항목을 생성합니다."),
retrieve=extend_schema(description="단일 예시 항목의 상세 정보를 반환합니다."),
update=extend_schema(description="기존 예시 항목을 업데이트합니다."),
partial_update=extend_schema(description="기존 예시 항목의 일부를 업데이트합니다."),
destroy=extend_schema(description="기존 예시 항목을 삭제합니다.")
)
class ExampleModelViewSet(ModelViewSet):
queryset = ExampleModel.objects.all()
serializer_class = ExampleModelSerializer
3) @extend_schema_serializer
- 직렬화 클래스에 대한 스키마 정보를 명시적으로 정의할 때 사용된다. 주로 스키마에서 특정 필드를 제외하거나, 요청과 응답 스키마에 추가 정보를 부착하는 데 유용하다.
- 적용 우선순위는 method > viewset > serializer 이다.
def extend_schema_serializer(
many: Optional[bool] = None,
exclude_fields: Optional[Sequence[str]] = None,
deprecate_fields: Optional[Sequence[str]] = None,
examples: Optional[Sequence[OpenApiExample]] = None,
extensions: Optional[Dict[str, Any]] = None,
component_name: Optional[str] = None,
) -> Callable[[F], F]:
"""
Decorator for the "serializer" kind. Intended for overriding default serializer behaviour that
cannot be influenced through :func:`@extend_schema <.extend_schema>`.
:param many: override how serializer is initialized. Mainly used to coerce the list view detection
heuristic to acknowledge a non-list serializer.
:param exclude_fields: fields to ignore while processing the serializer. only affects the
schema. fields will still be exposed through the API.
:param deprecate_fields: fields to mark as deprecated while processing the serializer.
:param examples: define example data to serializer.
:param extensions: specification extensions, e.g. ``x-is-dynamic``, etc.
:param component_name: override default class name extraction.
- 사용 방법
from drf_spectacular.utils import extend_schema_serializer, OpenApiExample
from rest_framework import serializers
@extend_schema_serializer(
# 스키마에서 제외하고 싶은 필드 목록
exclude_fields=('internal_field',),
# 직렬화 클래스에 대한 추가 설명
description='이것은 예시 직렬화 클래스입니다.'
)
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = ExampleModel
fields = '__all__'
# 내부 용도로만 사용되는 필드
internal_field = serializers.CharField()
@extend_schema_serializer(
exclude_fields=["password"],
examples=[
OpenApiExample(
"Valid example 1",
summary="short summary",
description="longer description",
value={
"is_superuser": True,
"username": "string",
"first_name": "string",
"last_name": "string",
"email": "user@example.com",
"is_staff": False,
"is_active": True,
"date_joined": "2021-04-18 04:14:30",
"user_type": "customer",
},
request_only=True, # view Layer에서 사용한 Example Object와 동일한 동작을 한다.
response_only=False,
),
],
)
class UserCustomSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = '__all__'
4) @extend_schema_field
- SerializerMethodField( 커스텀 필드) 혹은 기본 필드 형식이 명확하게 추론되지 않을 때 정의한 Field의 메타정보를 알 수 없다. 이때에는 extend_schema_field를 사용해 스키마에 사용될 필드의 타입힌트를 직접 정의할 수 있다.
- 드의 데이터 형식을 직접 지정하거나, 더 복잡한 구조를 가진 커스텀 Serializer를 사용할 때 명확하게 스키마에 정보를 표현할 수 있다.
from rest_framework import serializers
from drf_spectacular.utils import extend_schema_field
from drf_spectacular.types import OpenApiTypes
class UserSerializer(serializers.Serializer):
name = serializers.CharField(max_length=100)
# 커스텀 필드에 대해 OpenAPI 타입을 명시적으로 지정
@extend_schema_field(OpenApiTypes.UUID)
def get_custom_field(self):
pass
class CustomUserSerializer(serializers.HyperlinkedModelSerializer):
field_custom = serializers.SerializerMethodField(method_name="get_field_custom")
class Meta:
model = User
fields = '__all__'
@extend_schema_field(OpenApiTypes.DATETIME)
def get_field_custom(self, data):
return '2021-03-06 20:54:00'
5) 터미널에서 schema.yml 만들기
- 다른 개발자에게 오프라인으로 api의 정보에 대해 전달하기 위해서 schema.yml을 생성해 보낼 수 있다.
- 명령어 :
./manage.py spectacular --file schema.yml
- 실행시 아래와 같은 결과를 얻을 수 있다.
5. 상세 사용 방법
1) tag, summary, description
- tags :
- swagger에 어떤 부분에 tag 되어있을건지 정한다. 기본적으로 django의 app name에따라 default로 정해진다.
- API를 그룹핑하는데 사용합니다. 리스트 형태로 여러 태그를 지정할 수 있다.
- summary :
- API의 간략한 요약 정보를 제공한다.
- description :
- API의 상세 설명을 제공합니다. 여러 줄에 걸쳐 상세 정보를 기술할 수 있다.
class TistoryView(APIView):
@extend_schema(
tags=['files'],
summary="요약줄",
description="""설명란<br/>
html 태그를 사용할 수도 있습니다. """,
)
def get(self, request):
...
from django.urls import path
from drf_spectacular.utils import extend_schema
from rest_framework import serializers, views, viewsets
from rest_framework.response import Response
class IssueSerializer(serializers.Serializer):
issue_id = serializers.IntegerField()
description = serializers.CharField()
@extend_schema(tags=['고객 서비스'], summary='이슈 해결 요청', description='고객이 겪고 있는 문제를 해결하기 위한 상세 액션을 처리합니다.')
class IssueResolutionView(viewsets.GenericViewSet):
serializer_class = IssueSerializer
@extend_schema(tags=['이슈 해결'], responses={200: IssueSerializer})
def resolve_issue(self, request, *args, **kwargs):
# 가상의 로직으로 이슈 해결 프로세스를 표현
return Response({'issue_id': 123, 'description': '고객 문제 해결 완료'})
urlpatterns = [
path('resolve/', IssueResolutionView.as_view({'get': 'resolve_issue'})),
]
2) parameters
- API 엔드포인트의 입력값을 정의할 때 핵심적으로 활용되는 요소이다. 정확히 말하자면, parameters는 API 호출 시 요청에 포함되는 쿼리 파라미터, 헤더, 경로 변수 등을 명시하는 데 사용된다.
- get, post, put, fetch, delete 등 요청에 요구되는 입력 변수들을 명시할 수 있으며, 부가적인 설명과 필수 요소 등을 설정할 수 있다.
# models.py
from django.db import models
class Issue(models.Model):
description = models.TextField()
customer_id = models.CharField(max_length=100)
issue_type = models.CharField(max_length=100)
def __str__(self):
return f"Issue {self.id}: {self.description[:50]}"
# views.py
from rest_framework import serializers, viewsets
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter
from .models import Issue
class IssueSerializer(serializers.ModelSerializer):
class Meta:
model = Issue
fields = ["id", "description", "customer_id", "issue_type"]
@extend_schema(
tags=["고객 서비스"],
parameters=[
OpenApiParameter(
name="customer_id", description="고객 ID", required=True, type=str
),
OpenApiParameter(
name="issue_type", description="이슈 타입", required=True, type=str
),
],
)
class IssueResolutionViewSet(viewsets.ModelViewSet):
queryset = Issue.objects.all()
serializer_class = IssueSerializer
- 위와 같은 코드를 실행할 경우, 아래와 같은 화면을 확인할 수 있다.
3) request
- 해당 api에서 받을 request 데이터를 처리할 serializer를 정할 수 있다.
- 주로 복잡하거나 커스텀된 직렬화 형태를 가진 API 엔드포인트에서 요구될 수 있다.
- 그러나 이런 방법은 serialier_class를 사용 할 수 있는 GenericAPIView 이상부터 가능하다. 즉 일반 APIView는 사용할 수 없다.
class TistoryView(GenericAPIView):
serializer_class = TistorySerializer
@extend_schema(
request=TistorySerializer,
)
def post(self, request, *args, **kwargs):
pass
- 하지만, inline_serializer를 사용하면 정의할 수 있다.
- 예제 1 : 사용자 회원가입 API 사용자가 회원가입할 때, 이름, 이메일, 비밀번호 등을 입력해야 한다. 그러나, 관리자가 직접 사용자를 생성할 경우 추가로 role 필드를 지정할 수 있다.
from drf_spectacular.utils import extend_schema, inline_serializer
from rest_framework import serializers, views
@extend_schema(request=inline_serializer(
name='SignUpSerializer',
fields={
'name': serializers.CharField(),
'email': serializers.EmailField(),
'password': serializers.CharField(),
'role': serializers.ChoiceField(choices=['user', 'admin'], default='user', required=False)
}
))
class SignUpView(views.APIView):
pass
- 예제 2 : 장바구니 아이템 추가 API 사용자가 장바구니에 상품을 추가할 때, 상품 ID와 구매 수량을 요청 본문에 포함해야 한다. 여기서는 복수의 상품을 한 번에 추가하는 경우를 고려하여 many=True 옵션을 사용한다.
@extend_schema(request=inline_serializer(
name='CartItemAddSerializer',
fields={
'product_id': serializers.IntegerField(),
'quantity': serializers.IntegerField()
},
many=True # 여러 개의 상품 입력 허용
))
class CartItemAddView(views.APIView):
pass
4) responses
- api의 response를 정할 수 있다.
- dictionary형태로 status_code: response 형태로 적용한다.
- 그러나 중복된 status_code의 여러 가지 형태의 response는 보여줄 수 없다.
- 특정 HTTP 상태 코드에 대한 응답을 커스텀할 수 있으며, 에러 응답 또는 성공 응답에 대한 세부 정보를 명시한다.
- responses 옵션의 핵심 파라미터 :
1. 상태 코드
- Key 값으로 사용 : 응답 옵션에서 상태 코드(예: 200, 404, 400 등)는 사전 키(Dictionary Key)로 사용되어 해당 HTTP 응답 코드에 대응하는 응답의 내용을 정의한다.
2. 응답 시리얼라이저 또는 스키마
- 스키마 해석 : 각 상태 코드에 해당하는 응답을 설명하기 위해 사용되는 시리얼라이저 또는 스키마입니다. 이를 통해 응답 데이터의 구조와 예상되는 데이터 유형을 명확히 할 수 있다.
3. OpenApiResponse
- 응답 커스텀 : OpenApiResponse 객체를 사용함으로써 응답에 대한 사전 정의된 텍스트 설명 또는 추가 메타데이터를 제공할 수 있다. 이는 API 사용자에게 요청의 결과를 더욱 명확하게 이해시키는 데 도움이 된다.
- 사용 예 : OpenApiResponse(response=YourSerializer, description='성공적으로 수행됨')과 같이 사용할 수 있다.
- 예시 :
- 아래 예제에서는 200, 400 상태 코드에 대한 응답 설정을 보여준다. 200의 경우 ExampleSerializer가 응답 구조를 정의하고, 추가적인 설명 '성공 응답'을 제공한다. 400의 경우는 특정 시리얼라이저 없이 설명만을 제공한다.
from drf_spectacular.utils import extend_schema, OpenApiResponse
from rest_framework import serializers, views
class ExampleSerializer(serializers.Serializer):
message = serializers.CharField()
@extend_schema(
responses={
200: OpenApiResponse(response=ExampleSerializer, description='성공 응답'),
400: OpenApiResponse(description='잘못된 요청 처리')
}
)
class ExampleView(views.APIView):
pass
- 사용 방법 :
class TistoryView(GenericAPIView):
serializer_class = TistorySerializer
@extend_schema(
request=None,
responses={
200: TistorySerializer,
400: None
}
)
def post(self, request, *args, **kwargs):
pass
- 이 예제에서는 responses 옵션을 사용하여 HTTP 200 상태 코드에 대한 응답을 YourSerializer로 지정한다.
from drf_spectacular.utils import extend_schema
from rest_framework.views import APIView
from .serializers import YourSerializer
class YourAPIView(APIView):
@extend_schema(responses={200: YourSerializer})
def get(self, request, *args, **kwargs):
# 여기에 HTTP GET 요청 로직 구현
pass
- 이 예제에서는 200과 400 상태 코드에 대한 응답으로 SuccessSerializer와 ErrorSerializer를 각각 지정하고, 응답에 대한 설명을 추가한다.
from drf_spectacular.utils import extend_schema, OpenApiResponse
from rest_framework.views import APIView
from .serializers import SuccessSerializer, ErrorSerializer
class YourAPIView(APIView):
@extend_schema(
responses={
200: OpenApiResponse(response=SuccessSerializer, description='성공 응답'),
400: OpenApiResponse(response=ErrorSerializer, description='잘못된 요청')
}
)
def post(self, request, *args, **kwargs):
# 여기에 HTTP POST 요청 로직 구현
pass
5) examples
- examples를 사용하여 API의 예시를 정의할 수 있다.
- OpenAPiExaple을 이용해 상세 내용을 정의할 수 있다.
- OpenApiExample 클래스의 파라메터 :
- name: 키값(중복된 값이 있을시, 마지막으로 정의된 값으로 고정), 예제의 고유 이름이다. 이를 통해 예제를 식별할 수 있다.
- summary: 예제에 대해 짧게 설명한다.
- description: 예제에 대한 자세한 설명이다.
- value: 예제의 실제 값이다. 대부분의 경우, dict 형태로 입력된다.
- request_only: 이 예제가 요청에만 사용될지 여부를 나타낸다. (True / False)
- response_only: 이 예제가 응답에만 사용될지 여부를 나타낸다. (True / False)
- status_code: 예제가 표시될 때 HTTP 상태 코드를 명시할 수 있다. 이는 주로 응답 예제에 유용하다.
from drf_spectacular.utils import extend_schema, OpenApiExample
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['POST'])
@extend_schema(
examples=[
OpenApiExample(
name='주문 생성 요청 예시',
summary='주문 생성 요청',
description='새로운 주문을 생성하기 위한 요청의 예시입니다.',
value={
'item': '에스프레소',
'quantity': 1
},
request_only=True, # 요청 예시에만 사용됨
),
OpenApiExample(
name='성공적인 주문 생성',
summary='주문 생성 성공 예시',
description='새로운 주문이 성공적으로 생성되었을 때의 예시 응답입니다.',
value={
'order_id': 124,
'item': '에스프레소',
'quantity': 1,
'price': '4000원',
'ordered_at': '2023-04-01T11:30:00Z'
},
response_only=True,
status_codes=['201'], # HTTP 상태 코드를 배열 형태로 수정
)
]
)
def create_order(request):
# 실제 주문 생성 로직
data = {
'order_id': 124,
'item': '에스프레소',
'quantity': 1,
'price': '4000원',
'ordered_at': '2023-04-01T11:30:00Z'
}
return Response(data, status=201) # 예제와 일치하는 실제 응답 반환
6) 파일 업로드
- spectacular settings에 COMPONENT_SPLIT_REQUEST: True 를 설정한다.
- APIView에 parser_classes를 추가하면 파일 업로드가 가능하다.
SPECTACULAR_SETTINGS = {
...
'COMPONENT_SPLIT_REQUEST': True
}
class TistoryView(APIView):
parser_classes = (MultiPartParser,)
@extend_schema(
request=inline_serializer(
name="upload example",
fields={
"file": serializers.FileField(),
},
)
)
def post(self, request, *args, **kwargs):
pass
7) preprocessing_hooks
- API 스키마 생성 전 데이터를 전처리하는 데 사용되는 옵션 중 하나이다.
- 예시 : 특정 API 경로를 스키마에서 제외하기
- 프로젝트에는 여러 API 엔드포인트가 있고, 일부 엔드포인트를 API 문서에서 제외하고 싶다고 가정한다.
- 이 때 PREPROCESSING_HOOKS를 이용해 특정 조건에 맞는 엔드포인트를 자동으로 스키마에서 제외할 수 있다.
1. hook 함수 구현
# preprocessing_hook.py 파일에 아래 내용 코딩
def remove_private_paths(schema):
"""
API 스키마에서 특정 경로를 제외하는 hook function입니다.
"""
private_paths = ['/api/private/', '/api/internal/'] # 제외할 경로 목록
paths = dict(schema['paths'])
for path in private_paths:
paths.pop(path, None) # 경로가 존재하면 제거
schema['paths'] = paths
return schema
2. 설정에 hook 추가
- settings.py 파일에 SPECTACULAR_SETTINGS 딕셔너리 내에 PREPROCESSING_HOOKS 키를 설정하여 만든 hook을 연결한다.
# settings.py에 아래의 설정 추가
SPECTACULAR_SETTINGS = {
'PREPROCESSING_HOOKS': [
'your_project.preprocessing_hook.remove_private_paths',
],
}
- 예시 : api/v1 및 api/v2 경로 제외하기
- 어떤 웹 어플리케이션에서 다양한 버전의 API를 운영하고 있으며, 새로 출시되는 drf-spectacular 기반의 API 문서에서는 오직 최신 버전인 api/v3만을 노출하고 싶다고 가정 한다.
# preprocessing_hooks.py 파일에 구현
from drf_spectacular.plumbing import preprocess_schema
def exclude_old_api_versions(schema):
"""
'api/v1' 및 'api/v2' 경로를 스키마에서 제외하는 프리프로세싱 훅
"""
# 스키마에서 'paths' 키를 가져옵니다.
paths = schema.get('paths', {})
# 제외할 경로 목록을 정의합니다.
excluded_paths = ['api/v1/', 'api/v2/']
# 제외할 경로가 포함된 모든 엔트리를 삭제합니다.
for path in list(paths.keys()):
for excluded_path in excluded_paths:
if path.startswith(excluded_path):
del paths[path]
# 수정된 'paths'를 스키마에 적용합니다.
schema['paths'] = paths
return schema
# settings.py 파일에 추가
SPECTACULAR_SETTINGS = {
'PREPROCESSING_HOOKS': [
'my_project.preprocessing_hooks.exclude_old_api_versions' # 경로는 실제 프로젝트의 구조에 맞게 조정해야 합니다.
],
}
8) 인증 방식 설정하기
1. 기본 설정 :
- settings.py에 SPECTACULAR_SETTINGS 딕셔너리를 정의하여 drf-spectacular의 기본 설정을 조정할 수 있다.
- 아래 예시에서 'SECURITY' 설정은 Bearer 토큰을 사용한 인증 방식을 문서에 포함시키도록 한다.
SPECTACULAR_SETTINGS = {
...
'SECURITY': [{
'BearerAuth': [],
}]
...
}
2. 사용자 정의 토큰 타입 명시하기 :
- 이 설정은 Bearer Authentication이 JWT를 사용함을 명시한다. bearerFormat 필드를 통해 API 소비자에게 사용되는 토큰의 형식(JWT)을 명확하게 알려준다.
# settings.py
SPECTACULAR_SETTINGS = {
...
'SECURITY': [{
'BearerAuth': {
'type': 'http',
'scheme': 'bearer',
'bearerFormat': 'JWT', # JWT 토큰 사용 명시
}
}]
...
}
3. 여러 인증 방식 지정하기 :
- 이 설정은 시스템이 Bearer Token과 별도의 API 키를 통해 인증을 지원한다.
# settings.py
SPECTACULAR_SETTINGS = {
...
'SECURITY': [
{'BearerAuth': []}, # 기본 Bearer Token 인증
{
'ApiKeyAuth': { # API 키를 통한 추가 인증 방식
'type': 'apiKey',
'in': 'header',
'name': 'X-API-KEY'
}
}
]
...
}
4. OAuth2 인증 정보 추가하기 :
- 이 설정을 통해 OAuth2 authorizationCode 방식을 사용하는 인증 방식을 문서화할 수 있다. 사용자는 authorizationUrl을 통해 인증을 받고, tokenUrl에서 토큰을 받아 해당 API 시스템에 접근할 수 있는 권한(읽기, 쓰기)을 부여받는다.
# settings.py
SPECTACULAR_SETTINGS = {
...
'SECURITY': [{
'OAuth2': {
'type': 'oauth2',
'flows': {
'authorizationCode': {
'authorizationUrl': 'https://example.com/oauth/authorize',
'tokenUrl': 'https://example.com/oauth/token',
'scopes': {
'read': '읽기 권한',
'write': '쓰기 권한'
}
}
}
}
}]
...
}
2. OpenApiAuthenticationExtension :
- 공식 문서에는 아래와 같이 정의되어 있다.
- 구현하기 어려운 복잡한 문제들을 내포하고 있기 때문에, 사용하지 않을 것을 권장한다.
- 하지만 대안으로 OpenApiAuthenticationExtension 클래스를 활용하는 방식이 제시되고 있다.
# STRONGLY DISCOURAGED (with the exception for the djangorestframework-api-key library)
# please don't use this anymore as it has tricky implications that
# are hard to get right. For authentication, OpenApiAuthenticationExtension are
# strongly preferred because they are more robust and easy to write.
# However if used, the list of methods is appended to every endpoint in the schema!
'SECURITY': [],
- spectacular은 다양한 DRF 인증 클래스를 OpenAPI 3 스키마에 맞게 변환하기 위해 OpenApiAuthenticationExtension 확장 기능을 제공한다. 커스텀 인증 방식 혹은 타사 인증 패키지를 사용하는 경우, 이 클래스를 상속받아 구현할 수 있다.
- 위의 코드는 path.to.CustomAuthenticationClass에 정의된 커스텀 인증 방식을 문서화하는 확장 클래스의 예를 보여준다.
from drf_spectacular.extensions import OpenApiAuthenticationExtension
class CustomAuthScheme(OpenApiAuthenticationExtension):
target_class = 'path.to.CustomAuthenticationClass' # 커스텀 인증 클래스 경로
name = 'CustomAuth' # 스키마에 표시될 인증 이름
def get_security_definition(self, auto_schema):
return {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT" # JWT 토큰에 대한 설명
}
- JWT 토큰을 사용하여 사용자를 인증하는 API를 개발하고 있고, 이를 drf-spectacular를 이용해 문서화하고 싶다고 가정 한다면 위와 동일한 settings.py 설정에 아래와 같이 정의 할 수 있다.
# custom_auth_scheme.py
from drf_spectacular.extensions import OpenApiAuthenticationExtension
class JWTAuthScheme(OpenApiAuthenticationExtension):
target_class = 'django.contrib.auth.backends.ModelBackend' # 사용 중인 인증 백엔드
name = 'JWTAuth'
def get_security_definition(self, auto_schema):
return {
"type": "http",
"scheme": "bearer",
"bearerFormat": "JWT"
}
8) Permission
- SPECTACULAR_SETTINGS의 SERVE_PERMISSIONS와 SERVE_AUTHENTICATION 설정을 조정함으로써 API 접근 권한을 설정해 일부 사용자에게만 문서를 노출하거나, 특정 조건에서만 접근을 허용하는 기능을 제공할 수 있다.
- 예시 :
1. 관리자만 API 문서 접근
# settings.py
SPECTACULAR_SETTINGS = {
'SERVE_PERMISSIONS': ['rest_framework.permissions.IsAdminUser'],
}
2. 커스텀 Permission 사용 - 1
- 슈퍼유저만 접근 가능하다.
# permissions.py
from rest_framework import permissions
class IsSuperUser(permissions.BasePermission):
"""
슈퍼유저만 접근 가능한 Permission
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_superuser)
# settings.py
# 맞춤 Permission 클래스를 SERVE_PERMISSIONS에 추가합니다.
SPECTACULAR_SETTINGS = {
'SERVE_PERMISSIONS': ['path.to.permissions.IsSuperUser'],
}
# urls.py
from django.urls import path
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
urlpatterns = [
path('schema/', SpectacularAPIView.as_view(), name='schema'),
# 슈퍼유저만 API 문서를 볼 수 있습니다.
path('docs/', SpectacularSwaggerView.as_view(url_name='schema'), name='docs'),
]
3. 커스텀 Permission 사용 - 2
- 디버그 모드에만 접근이 가능하게 한다.
from django.conf import settings
from rest_framework import permissions
class IsDebugMode(permissions.BasePermission):
"""
settings.DEBUG가 True일 때만 API 문서에 접근을 허용합니다.
"""
def has_permission(self, request, view):
return settings.DEBUG
SPECTACULAR_SETTINGS = {
'TITLE': '마이쉽단 API',
...
'DISABLE_ERRORS_AND_WARNINGS': True, # warning과 error 안뜨게 설정
'SERVE_PERMISSIONS': ['core.permissions.IsDebugMode'],
}
6. ModelViewSet의 기본 action과 custom action을 문서화 하는 방법
- ModelViewSet에 정의된 action들을 문서화 하고, custom action을 문서화 하는 방법
from django.contrib.auth.models import User
from django.http import HttpResponse
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiExample, OpenApiParameter, extend_schema_view
from drf_spectacular.utils import extend_schema
from rest_framework.decorators import action
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from rest_framework import serializers, status
class UserSerializer(serializers.ModelSerializer):
user_type = serializers.CharField(help_text="회원의 유형값을 받습니다.", default="customer")
class Meta:
model = User
depth = 1
fields = "__all__"
@extend_schema_view(
list=extend_schema(summary="이런식으로 class레벨 데코레이터로 문서 커스터마이징 가능하다.", tags=["User"]),
retrieve=extend_schema(summary="사용자 세부 정보 조회", tags=["User"]),
update=extend_schema(summary="사용자 정보 갱신", tags=["User"]),
partial_update=extend_schema(summary="사용자 정보 부분 갱신", tags=["User"]),
i_am_custom_api=extend_schema(
summary="@action API도 마찬가지로 class 데코레이터로 문서 커스터마이징 가능하다.",
tags=["User"],
request=UserSerializer,
responses={status.HTTP_200_OK: UserSerializer},
),
manage_likes=extend_schema(
summary="사용자 좋아요 관리",
description="특정 사용자의 좋아요 수 증감",
request=None,
responses={status.HTTP_200_OK: UserSerializer},
parameters=[
OpenApiParameter(name="action", description="좋아요 또는 좋아요 취소 액션", required=True, type=OpenApiTypes.STR, examples=[]),
],
tags=["User"]
),
)
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
@extend_schema(
tags=["User"],
summary="method레벨 데코레이터도 가능",
parameters=[
OpenApiParameter(name="a_param", description="QueryParam1 입니다.", required=False, type=str),
OpenApiParameter(
name="date_param",
type=OpenApiTypes.DATE,
location=OpenApiParameter.QUERY,
description="Filter by release date",
examples=[
OpenApiExample(
"이것은 Query Parameter Example입니다.",
summary="short optional summary",
description="longer description",
value="1993-08-23",
),
],
),
],
examples=[
OpenApiExample(
request_only=True,
summary="이거는 Request Body Example입니다.",
name="success_example",
value={
"username": "root",
"password": "django_1234",
"first_name": "성렬",
"last_name": "김",
"email": "user@example.com",
},
),
],
)
def create(self, request: Request, *args, **kwargs) -> Response:
response: HttpResponse = super().create(request, *args, **kwargs)
return response
@action(
detail=False, url_path="custom-action-api",
)
def i_am_custom_api(self, request: Request, *args, **kwargs):
return Response(data={"hi": "i am custom api"})
@action(detail=True, methods=['post'], url_path='manage-likes')
def manage_likes(self, request, pk=None):
user = self.get_object()
action_type = request.data.get("action")
if action_type == "like":
user.likes += 1
elif action_type == "unlike":
user.likes -= 1
user.save()
return Response({"likes": user.likes})
7. request_only, response_only를 사용한 문서 정의 방법
- 옵션 설명 :
- responses를 통해 예상되는 HTTP 응답 상태 코드에 대한 상세 설명과 함께 해당 응답의 형식을 보여준다.
- parameters를 이용해 API에 추가적인 쿼리 파라미터를 문서화하고, 이 파라미터가 필수 항목인지, 타입은 무엇인지 정의한다.
- description으로 메서드의 상세한 설명을 추가한다.
- request 옵션으로 요청 시 기대되는 스키마 형태를 지정한다.
- methods를 사용하여 이 API가 지원되는 HTTP 메소드를 제한한다.
- examples를 통해 요청과 응답에 대한 구체적인 예시를 제공하여 일반적인 사용 사례를 이해하기 쉽도록 돕는다.
from drf_spectacular.utils import extend_schema, OpenApiExample, OpenApiParameter, OpenApiResponse
from drf_spectacular.types import OpenApiTypes
from rest_framework.request import Request
from rest_framework.response import Response
@extend_schema(
tags=["User"], # 이 API에 적용할 태그 정보입니다.
summary="method레벨 데코레이터도 가능", # 보다 상세한 요약설명을 제공합니다.
responses={ # 기대되는 응답 형상에 대한 정보입니다.
200: OpenApiResponse(description='성공적인 사용자 생성', response=OpenApiTypes.OBJECT),
400: OpenApiResponse(description='잘못된 요청', response=OpenApiTypes.OBJECT),
401: OpenApiResponse(description='인증 실패', response=OpenApiTypes.OBJECT)
},
# API 호출에 사용될 추가적인 쿼리 파라미터를 정의합니다.
parameters=[
OpenApiParameter(name='sample_param', description='샘플 추가 파라미터', required=False, type=OpenApiTypes.STR),
],
description="API를 통해 새로운 사용자를 생성합니다. 성공, 실패, 요청 사항에 대한 예시가 포함됩니다.",
request=OpenApiTypes.OBJECT, # 요청 시에 기대되는 스키마를 단순화하여 표시합니다.
methods=['POST'], # 해당 API의 HTTP 메소드를 제한합니다.
examples=[ # 요청과 응답의 예시를 제공합니다.
OpenApiExample(
name="success_example",
value={
"username": "root",
"password": "django_1234",
"first_name": "성렬",
"last_name": "김",
"email": "user@example.com",
},
),
OpenApiExample(
request_only=True, # 요청 예제 명시
name="invalid_example1",
summary="비밀번호 너무 쉬움 예제",
description="비밀번호가 보안 기준에 미치지 못합니다.",
value={
"username": "root23",
"password": "1234",
"first_name": "성렬",
"last_name": "김",
"email": "user@example.com",
},
),
OpenApiExample(
request_only=True,
name="invalid_example2",
summary="이름 필수 입력 예제",
description="첫 번째 이름 입력이 누락되었습니다.",
value={
"username": "root434",
"password": "django_1234",
"first_name": "",
"last_name": "김",
"email": "user@example.com",
},
),
OpenApiExample(
response_only=True, # 응답 예제 명시
name="success_example2",
summary="사용자 생성 성공 예제",
description="성공적으로 사용자가 생성된 경우의 예제 응답입니다.",
value={
"username": "root434",
"password": "django_1234",
"first_name": "성렬",
"last_name": "김",
"email": "user@example.com",
},
),
],
)
def create(self, request: Request, *args, **kwargs) -> Response:
# API 구현 로직
pass
8. Query Param에 Example 목록 정의하는 방법
- 주요 옵션 설명 :
1) deprecated: 이를 True로 설정하면 해당 파라미터가 더 이상 사용되지 않음을 나타내어, API 사용자가 새로운 대체 파라미터나 방법을 사용하도록 유도할 수 있다.
2) style 및 explode: 이 설정들은 주로 파라미터가 어떻게 serialize되고 API에 전달되는지를 제어한다. 예를 들어, 배열이나 객체를 여러 파라미터로 분해해서 전달할 수 있다.
3) allowEmptyValue: API 사용 시 특정 파라미터가 값을 가지지 않는 경우를 허용할지 여부를 결정한다. 예를 들어, 값을 설정하지 않고 파라미터만 전달하여 기본 동작을 유도할 수 있다.
@extend_schema(
tags=["사용자"],
summary="method레벨 데코레이터도 가능",
parameters=[
OpenApiParameter(
name="a_param",
description="QueryParam1 입니다.",
required=False,
type=str,
# 추가 옵션
deprecated=False, # 이 파라미터가 더 이상 사용되지 않음을 나타냄
style='form', # 이 파라미터가 serialize되는 스타일을 정의 (예: "form", "deepObject")
explode=True, # 스타일이 'form'이고, 'array' 또는 'object' 타입인 경우, 개별 요소를 별도 파라미터로 전달할지 여부
allowEmptyValue=False, # 파라미터 값이 비어 있어도 되는지 여부
),
OpenApiParameter(
name="date_param",
type=OpenApiTypes.DATE,
location=OpenApiParameter.QUERY,
description="Filter by release date",
examples=[
OpenApiExample(
"이것은 Query Parameter Example입니다.",
summary="short optional summary1",
description="longer description",
value="1993-08-23",
),
OpenApiExample(
"이것은 Query Parameter Example2입니다.",
summary="short optional summary2",
description="longer description",
value="1993-08-23",
),
OpenApiExample(
"이것은 Query Parameter Example3입니다.",
summary="short optional summary3",
description="longer description",
value="1993-08-23",
),
],
# 여기에 추가 옵션을 넣을 수 있지만 위 예제를 참고해 주세요.
),
],
)
def create(self, request: Request, *args, **kwargs) -> Response:
# 함수 내용
...
9. 다른 파일로 분리하여 사용하는 방법
- 문서화 코드를 작성하다보면, 실재 동작하는 코드보다 더 많은 코드가 생성될 수 있다.
- 이런 경우, 새로운 파일에 데코레이터로 새로 만들어 사용하거나, example을 정의하고 views.py에서 쉽게 호출하는 방식으로 사용할 수 있다.
- example 분리하는 방법 :
# example_objects.py 파일
from drf_spectacular.utils import OpenApiExample
example1 = OpenApiExample(
name="Query Parameter Example 1",
description="이것은 Query Parameter Example1입니다.",
value="1993-08-23",
)
example2 = OpenApiExample(
name="Query Parameter Example 2",
description="이것은 Query Parameter Example2입니다.",
value="1994-09-24",
)
example3 = OpenApiExample(
name="Query Parameter Example 3",
description="이것은 Query Parameter Example3입니다.",
value="1995-10-25",
)
from drf_spectacular.utils import extend_schema, OpenApiParameter
from .example_objects import example1, example2, example3 # 예제 객체 import
@extend_schema(
tags=["사용자"],
summary="method레벨 데코레이터도 가능",
parameters=[
OpenApiParameter(
name="a_param",
description="QueryParam1 입니다.",
required=False,
type=str,
# 여기에 추가 옵션들...
),
OpenApiParameter(
name="date_param",
type=OpenApiTypes.DATE,
location=OpenApiParameter.QUERY,
description="Filter by release date",
examples=[example1, example2, example3], # 사용 예제 객체들
# 추가 옵션들...
),
],
)
def create(self, request, *args, **kwargs):
# 함수 내용
- 데코레이터로 분리하는 방법
# schema_decorators.py
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes, OpenApiExample
def custom_extend_schema(_func=None, **kwargs):
"""사용자 정의 extend_schema 데코레이터"""
def decorator(func):
return extend_schema(
# 사용자 맞춤 설정을 여기에 추가
tags=["사용자"],
summary="method레벨 데코레이터도 가능",
parameters=[
OpenApiParameter(
name="a_param",
description="QueryParam1 입니다.",
required=False,
type=str,
# 추가 옵션
deprecated=False,
style='form',
explode=True,
allowEmptyValue=False,
),
OpenApiParameter(
name="date_param",
type=OpenApiTypes.DATE,
location=OpenApiParameter.QUERY,
description="Filter by release date",
examples=[
OpenApiExample(
"이것은 Query Parameter Example입니다.",
summary="short optional summary1",
description="longer description",
value="1993-08-23",
),
# 추가 예시
],
# 추가 옵션
),
],
**kwargs # 추가적인 데코레이터 설정을 위한 가변 인자
)(func)
return decorator if _func is None else decorator(_func)
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .schema_decorators import custom_extend_schema # custom_extend_schema import
@custom_extend_schema
class ExampleView(APIView):
def get(self, request, *args, **kwargs):
# Your view logic here
return Response({"message": "This is an example response!"})
- reference :
https://gruuuuu.github.io/programming/openapi/
https://drf-spectacular.readthedocs.io/en/latest/
https://velog.io/@catveloper/DRF-Spectacular-%EC%82%AC%EC%9A%A9%EB%B2%95
https://mixedprograming.tistory.com/36
https://velog.io/@catveloper/DRF-Spectacular-%EC%82%AC%EC%9A%A9%EB%B2%95
https://whitepro.tistory.com/650
https://www.slideshare.net/ssuserc158fd/api-pdf-259231365