Django

drf_spectacular를 사용한 api docs 만들기

친구들안녕 2022. 11. 20. 17:11

목차

    서론

    필자가 drf_spectacularf를 사용하여 만들었던 docs 페이지를 기억하기 위해 작성하는 페이지입니다.
    찾으시는 내용이 없을 수 있습니다.
    또한 정확히 알지 않고 필자의 경험으로만 이루어져 있기 때문에 없는 내용 및 틀린 내용이 존재할 수 있습니다.

    본론

    1. drf-spectacular settings

    settings.py에 API docs에 보여질 title, description, version을 지정할 수 있습니다.
    이외의 기타 설정값들도 지정할수 있으니 링크 를 참조하시길 바랍니다.

    SPECTACULAR_SETTINGS = {
        'TITLE': 'TITLE',
        'DESCRIPTION': 'DESCRIPTION',
        'VERSION': '1.0.0',
    }

    2. extend_schema

    - 기본 사용법

    사용할 api의 method 함수 부분에 @extend_schema 데코레이터를 추가하여 사용할 수 있습니다.

    class TistoryView(APIView):
    
        @extend_schema(
        ...
        )
        def get(self, request):
            ...


    3. tags, summary, description

    tags: swagger에 어떤 부분에 tag 되어있을건지 정한다.
    기본적으로 django의 app name에따라 default로 정해진다.

    summary: url 옆에 요약줄 부분에 내용을 쓸 수 있다.

    description: 접은글을 펴면 상단에 위치한 description을 볼 수 있다.

    class TistoryView(APIView):
    
        @extend_schema(
            tags=['files'],
            summary="요약줄",
            description="""설명란<br/>
                html 태그를 사용할 수도 있습니다. """,
        )
        def get(self, request):
            ...


    tas, summary, description 적용 이미지

    4. parameters

    paramters를 사용하여 url의 parameter를 추가할 수 있습니다.
    또한 아래 이미지와 같이 parameter를 추가하여, 테스트 할 수 있습니다.

    class TistoryView(APIView):
    
        @extend_schema(
            ...
            parameters=[
                OpenApiParameter(
                    name="name",
                    type=str,
                    description="description",
                    required=False,
                )
            ]
        )
        def get(self, request):
            pass


    5. request

    아래와 같이 request에 Serailizer를 사용하여 이 api에서 받을 request 데이터를 자동으로 정할 수 있습니다.
    그러나 이런 Serializer를 사용한 docs 자동화는 serialier_class를 사용 할 수 있는 GenericAPIView 이상부터 가능합니다.
    즉 일반 APIView는 사용이 불가능 합니다.

    class TistoryView(GenericAPIView):
        serializer_class = TistorySerializer
    
        @extend_schema(
            request=TistorySerializer,
        )
        def post(self, request, *args, **kwargs):
            pass



    물론 다른 방법으로 inline_serializer를 사용하여 정의할 수 있습니다.

    class TistoryView(APIView):
    
        @extend_schema(
            request=inline_serializer(
                name='inline serializer',
                fields={
                    "name": serializers.CharField(default="name"),
                    "integer": serializers.IntegerField(default=100)
                }
            )
        )
        def post(self, request, *args, **kwargs):
            pass
    

    비교를 위해 default 값을 넣었습니다

    inline_serializer를 사용하면, 일반적인 APIView를 사용해도 request 값을 정할 수 있습니다.

    6. responses

    responses를 사용하여 api의 response를 정할 수 있습니다.
    dictionary형태로 status_code: response 형태로 적용합니다.
    그러나 중복된 status_code의 여러 가지 형태의 response는 보여줄 수 없습니다.

    class TistoryView(GenericAPIView):
        serializer_class = TistorySerializer
    
        @extend_schema(
            request=None,
            responses={
                200: TistorySerializer,
                400: None
            }
        )
        def post(self, request, *args, **kwargs):
            pass

    7. examples

    examples를 사용하여 API의 예시를 추가할 수 있습니다.
    OpenAPiExaple을 사용하여 넣을 수 있습니다.
    parameters, requests에도 사용이 가능하나 필자는 response를 사용한 예시를 보여드리겠습니다.

    name: 키값(중복된 값이 있을시, 마지막으로 정의된 값으로 고정됩니다)
    summary: 선택 목록에 보일 내용
    value: response value
    status_code: response에서 고정될 status_code
    response_only: response에만 들어갈 내용인지

    class TistoryView(GenericAPIView):
        serializer_class = TistorySerializer
    
        @extend_schema(
            request=None,
            responses={
                200: TistorySerializer,
                400: OpenApiTypes.OBJECT
            },
            examples=[
                OpenApiExample(
                    name="400(1)",
                    summary="400(1)",
                    value={
                        "message": "400(1)",
                    },
                    status_codes=["400"],
                    response_only=True
                ),
                OpenApiExample(
                    name="400(2)",
                    summary="400(2)",
                    value={
                        "message": "400(2)",
                    },
                    status_codes=["400"],
                    response_only=True
                ),
            ]
        )
        def post(self, request, *args, **kwargs):
            pass

    주의할 사항으로는 responses에서 400의 값이 None이라면 examples가 적용되지 않으므로 OpenApiTypes.OBJECT를 넣었습니다.
    어떤 값이든 들어가면 됩니다.

    추가 팁 (파일 업로드)

    실패 사례

    먼저 일반적으로 사용되는 FileField drf-spectacular에서 적용되지 않습니다.

    class TistoryView(APIView):
        @extend_schema(
            request=inline_serializer(
                name="upload example",
                fields={
                    "file": serializers.FileField(),
                },
            )
        )
        def post(self, request, *args, **kwargs):
            pass


    성공사례

    spectacular settings에 COMPONENT_SPLIT_REQUEST: True와
    APIView에 parser_classes를 추가하면 파일 업로드가 가능해집니다.

    settings.py

    SPECTACULAR_SETTINGS = {
        ...
        'COMPONENT_SPLIT_REQUEST': True
    }

    views.py

    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

    파일 선택하는 버튼이 생성되었습니다