如何在Django REST Swagger中生成响应消息列表?

时间:2021-12-25 03:46:56

I have upgraded Django REST Framework to 3.5.0 yesterday because I need nice schema generation.

我昨天升级了Django REST框架到3.5.0,因为我需要很好的模式生成。

I am using Django REST Swagger to document my API but don't know how to list all possible response messages that an API endpoint provides.

我使用Django REST Swagger来记录我的API,但是不知道如何列出API端点提供的所有可能的响应消息。

It seems that there is automatic generation of success message corresponding to the action my endpoint is performing.

似乎有自动生成的成功消息对应于我的端点正在执行的操作。

So POST actions generate 201 response code, without any description.

所以POST actions生成201个响应代码,没有任何描述。

如何在Django REST Swagger中生成响应消息列表?

How would I go about adding all the response messages that my endpoint provides and give them some descriptions?

我该如何添加端点提供的所有响应消息并给它们一些描述呢?

I am using

我用

djangorestframework==3.5.0

djangorestframework = = 3.5.0

django-rest-swagger==2.0.7

django-rest-swagger = = 2.0.7

1 个解决方案

#1


16  

Ah, Finally got it.

啊,终于明白了。

But! This is hack on hack - and probably drf + drf swagger not support that; Basically the problem is not connected to the drf and drf swagger code, rather the openapi codec, see yourself:

但是!这是黑客攻击——而且很可能是drf + drf虚张声势,不支持这种攻击;基本上,问题与drf和drf swagger代码没有关联,而是openapi编解码器,请参见:

def _get_responses(link):
    """
    Returns minimally acceptable responses object based
    on action / method type.
    """
    template = {'description': ''}
    if link.action.lower() == 'post':
        return {'201': template}
    if link.action.lower() == 'delete':
        return {'204': template}
    return {'200': template}

The above code can be found at: openapi_codec/encode.py - github This is not connected in any way with drf or drf swagger - just for each link (eg.: GET /api/v1/test/) create a template with empty description.

上面的代码可以在:openapi_codec/encode处找到。py - github,这与drf或drf swagger没有任何关联——只是针对每个链接(例如。:获取/api/v1/test/)创建一个描述为空的模板。

Of course there's a possibility to overcome this issue. But as I said - this is hack on hack :) I will share an example with you:

当然有可能解决这个问题。但正如我所说——这是黑客攻击:)我将与你们分享一个例子:

docs_swagger.views.py

from rest_framework import exceptions
from rest_framework.permissions import AllowAny
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_swagger import renderers

from docs_swagger.schema_generator import CustomSchemaGenerator


def get_swagger_view(title=None, url=None):
    """
    Returns schema view which renders Swagger/OpenAPI.

    (Replace with DRF get_schema_view shortcut in 3.5)
    """
    class SwaggerSchemaView(APIView):
        _ignore_model_permissions = True
        exclude_from_schema = True
        permission_classes = [AllowAny]
        renderer_classes = [
            CoreJSONRenderer,
            renderers.OpenAPIRenderer,
            renderers.SwaggerUIRenderer
        ]

        def get(self, request):
            generator = CustomSchemaGenerator(title=title, url=url)  # this is altered line
            schema = generator.get_schema(request=request)
            if not schema:
                raise exceptions.ValidationError(
                    'The schema generator did not return a schema   Document'
                )
            return Response(schema)

    return SwaggerSchemaView.as_view()

What I do in the CustomSchemaGenerator is as follows:

我在CustomSchemaGenerator中的工作是:

docs_swagger.schema_generator.py

import urlparse
import coreapi
from rest_framework.schemas import SchemaGenerator

from openapi_codec import encode


def _custom_get_responses(link):
    detail = False
    if '{id}' in link.url:
        detail = True
    return link._responses_docs.get(
        '{}_{}'.format(link.action, 'list' if not detail else 'detail'),
        link._responses_docs
    )


# Very nasty; Monkey patching;
encode._get_responses = _custom_get_responses


class CustomSchemaGenerator(SchemaGenerator):

    def get_link(self, path, method, view):
        """
        Return a `coreapi.Link` instance for the given endpoint.
        """
        fields = self.get_path_fields(path, method, view)
        fields += self.get_serializer_fields(path, method, view)
        fields += self.get_pagination_fields(path, method, view)
        fields += self.get_filter_fields(path, method, view)

        if fields and any([field.location in ('form', 'body') for field in fields]):
            encoding = self.get_encoding(path, method, view)
        else:
            encoding = None

        description = self.get_description(path, method, view)

        if self.url and path.startswith('/'):
            path = path[1:]

        # CUSTOM
        data_link = coreapi.Link(
            url=urlparse.urljoin(self.url, path),
            action=method.lower(),
            encoding=encoding,
            fields=fields,
            description=description
        )

        data_link._responses_docs = self.get_response_docs(path, method, view)

        return data_link

    def get_response_docs(self, path, method, view):
        return view.responses_docs if hasattr(view, 'responses_docs') else {'200': {
            'description': 'No response docs definition found.'}
        }

And finally:

最后:

my_view.py

class TestViewSet(viewsets.ModelViewSet):
    queryset = Test.objects.all()
    serializer_class = TestSerializer

    responses_docs = {
        'get_list': {
            '200': {
                'description': 'Return the list of the Test objects.',
                'schema': {
                    'type': 'array',
                    'items': {
                        'type': 'object',
                        'properties': {
                            'id': {
                                'type': 'integer'
                            }
                        }
                    }
                }
            },
            '404': {
                'description': 'Not found',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'message': {
                            'type': 'string'
                        }
                    }
                },
                'example': {
                    'message': 'Not found.'
                }
            }
        },
        'get_detail': {
            '200': {
                'description': 'Return single Test object.',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'id': {
                            'type': 'integer'
                        }
                    }
                }
            },
            '404': {
                'description': 'Not found.',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'message': {
                            'type': 'string'
                        }
                    }
                },
                'example': {
                    'message': 'Not found.'
                }
            }
        }
    }

I consider this more like fun instead of a real solution. The real solution is probably impossible to achieve at the current state. Maybe you should ask the creators of drf swagger - do they have plans to support responses?

我认为这更像是乐趣而不是真正的解决方案。在目前的状态下,真正的解决方案可能是不可能实现的。也许你应该问问drf swagger的创作者——他们有支持回应的计划吗?

Anyway, the swagger UI: 如何在Django REST Swagger中生成响应消息列表?

总之,大摇大摆的界面:

Happy coding :)

编码快乐:)

#1


16  

Ah, Finally got it.

啊,终于明白了。

But! This is hack on hack - and probably drf + drf swagger not support that; Basically the problem is not connected to the drf and drf swagger code, rather the openapi codec, see yourself:

但是!这是黑客攻击——而且很可能是drf + drf虚张声势,不支持这种攻击;基本上,问题与drf和drf swagger代码没有关联,而是openapi编解码器,请参见:

def _get_responses(link):
    """
    Returns minimally acceptable responses object based
    on action / method type.
    """
    template = {'description': ''}
    if link.action.lower() == 'post':
        return {'201': template}
    if link.action.lower() == 'delete':
        return {'204': template}
    return {'200': template}

The above code can be found at: openapi_codec/encode.py - github This is not connected in any way with drf or drf swagger - just for each link (eg.: GET /api/v1/test/) create a template with empty description.

上面的代码可以在:openapi_codec/encode处找到。py - github,这与drf或drf swagger没有任何关联——只是针对每个链接(例如。:获取/api/v1/test/)创建一个描述为空的模板。

Of course there's a possibility to overcome this issue. But as I said - this is hack on hack :) I will share an example with you:

当然有可能解决这个问题。但正如我所说——这是黑客攻击:)我将与你们分享一个例子:

docs_swagger.views.py

from rest_framework import exceptions
from rest_framework.permissions import AllowAny
from rest_framework.renderers import CoreJSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_swagger import renderers

from docs_swagger.schema_generator import CustomSchemaGenerator


def get_swagger_view(title=None, url=None):
    """
    Returns schema view which renders Swagger/OpenAPI.

    (Replace with DRF get_schema_view shortcut in 3.5)
    """
    class SwaggerSchemaView(APIView):
        _ignore_model_permissions = True
        exclude_from_schema = True
        permission_classes = [AllowAny]
        renderer_classes = [
            CoreJSONRenderer,
            renderers.OpenAPIRenderer,
            renderers.SwaggerUIRenderer
        ]

        def get(self, request):
            generator = CustomSchemaGenerator(title=title, url=url)  # this is altered line
            schema = generator.get_schema(request=request)
            if not schema:
                raise exceptions.ValidationError(
                    'The schema generator did not return a schema   Document'
                )
            return Response(schema)

    return SwaggerSchemaView.as_view()

What I do in the CustomSchemaGenerator is as follows:

我在CustomSchemaGenerator中的工作是:

docs_swagger.schema_generator.py

import urlparse
import coreapi
from rest_framework.schemas import SchemaGenerator

from openapi_codec import encode


def _custom_get_responses(link):
    detail = False
    if '{id}' in link.url:
        detail = True
    return link._responses_docs.get(
        '{}_{}'.format(link.action, 'list' if not detail else 'detail'),
        link._responses_docs
    )


# Very nasty; Monkey patching;
encode._get_responses = _custom_get_responses


class CustomSchemaGenerator(SchemaGenerator):

    def get_link(self, path, method, view):
        """
        Return a `coreapi.Link` instance for the given endpoint.
        """
        fields = self.get_path_fields(path, method, view)
        fields += self.get_serializer_fields(path, method, view)
        fields += self.get_pagination_fields(path, method, view)
        fields += self.get_filter_fields(path, method, view)

        if fields and any([field.location in ('form', 'body') for field in fields]):
            encoding = self.get_encoding(path, method, view)
        else:
            encoding = None

        description = self.get_description(path, method, view)

        if self.url and path.startswith('/'):
            path = path[1:]

        # CUSTOM
        data_link = coreapi.Link(
            url=urlparse.urljoin(self.url, path),
            action=method.lower(),
            encoding=encoding,
            fields=fields,
            description=description
        )

        data_link._responses_docs = self.get_response_docs(path, method, view)

        return data_link

    def get_response_docs(self, path, method, view):
        return view.responses_docs if hasattr(view, 'responses_docs') else {'200': {
            'description': 'No response docs definition found.'}
        }

And finally:

最后:

my_view.py

class TestViewSet(viewsets.ModelViewSet):
    queryset = Test.objects.all()
    serializer_class = TestSerializer

    responses_docs = {
        'get_list': {
            '200': {
                'description': 'Return the list of the Test objects.',
                'schema': {
                    'type': 'array',
                    'items': {
                        'type': 'object',
                        'properties': {
                            'id': {
                                'type': 'integer'
                            }
                        }
                    }
                }
            },
            '404': {
                'description': 'Not found',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'message': {
                            'type': 'string'
                        }
                    }
                },
                'example': {
                    'message': 'Not found.'
                }
            }
        },
        'get_detail': {
            '200': {
                'description': 'Return single Test object.',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'id': {
                            'type': 'integer'
                        }
                    }
                }
            },
            '404': {
                'description': 'Not found.',
                'schema': {
                    'type': 'object',
                    'properties': {
                        'message': {
                            'type': 'string'
                        }
                    }
                },
                'example': {
                    'message': 'Not found.'
                }
            }
        }
    }

I consider this more like fun instead of a real solution. The real solution is probably impossible to achieve at the current state. Maybe you should ask the creators of drf swagger - do they have plans to support responses?

我认为这更像是乐趣而不是真正的解决方案。在目前的状态下,真正的解决方案可能是不可能实现的。也许你应该问问drf swagger的创作者——他们有支持回应的计划吗?

Anyway, the swagger UI: 如何在Django REST Swagger中生成响应消息列表?

总之,大摇大摆的界面:

Happy coding :)

编码快乐:)