Quba Quba - 15 days ago 10
reST (reStructuredText) Question

Django rest framework Api documentation Swagger 2.0

I am having a hard time configuring Swagger UI. Here are the very explanatory docs: https://django-rest-swagger.readthedocs.io/en/latest/

YAML docstrings are deprecated. Does somebody know how to configure Swagger UI (query parameters, etc) from within the python code?

If it's impossible for some strange reason. Is there any working alternative or is it the best for me to just go and write, api documentation by hand?

Answer

OK, found it. This is not ideal solution - but needed this for frontend (web and mobile) devs - and it do the job.

Basically the newest DRF and Swagger uses from rest_framework.schemas import SchemaGenerator for providing the docs for Swagger.

So I needed to little extend it:

# -*- coding: utf-8 -*-
import urlparse

import coreapi
from rest_framework.schemas import SchemaGenerator


class ParamsSchemaGenerator(SchemaGenerator):

    def get_link(self, path, method, callback, view):
        """
        Return a `coreapi.Link` instance for the given endpoint.
        """
        fields = self.get_path_fields(path, method, callback, view)
        fields += self.get_serializer_fields(path, method, callback, view)
        fields += self.get_pagination_fields(path, method, callback, view)
        fields += self.get_filter_fields(path, method, callback, view)
        fields += self.get_docs_fields(path, method, callback, view)  # this is the extended line;

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

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

        return coreapi.Link(
            url=urlparse.urljoin(self.url, path),
            action=method.lower(),
            encoding=encoding,
            fields=fields
        )

    # and this is fully custom additional docs method;
    def get_docs_fields(self, path, method, callback, view):
        fields = []
        if hasattr(view, 'docs_fields'):
            for field in view.docs_fields:
                field = coreapi.Field(
                    name=field.get('name'),
                    location=field.get('query'),
                    required=field.get('required'),
                    type=field.get('type'),
                    description=field.get('description')
                )
                fields.append(field)

        return fields

Then i need to define a function that will return the schemas with the generator defined above:

# -*- coding: utf-8 -*-
# monkey patching FTW!

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 kolomnie.core.schema.generator import ParamsSchemaGenerator


def get_params_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 = ParamsSchemaGenerator(title=title, url=url)
            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()

This is how i put it in the urls:

if settings.DEBUG:
    api_views = get_params_swagger_view(title='Some API')

And now little more magic, I defined a mixin for the view which stores the documentation fields:

# -*- coding: utf-8 -*-


class DocsMixin(object):
    """
    This mixin can be used to document the query parameters in GET
    if there's no other way to do it. Please refer to the: ParamsSchemaGenerator.get_links
    for more information;
    """
    docs_fields = []

And this is how I use it:

class BaseSearchResultsView(generics.GenericAPIView, SearchDocs):
    ....

Where SearchDocs is this:

class SearchDocs(DocsMixin):
    """
    Documents the get query in search;
    """
    docs_fields = [
        {
            'name': 'q',
            'location': 'query',
            'required': False,
            'type': 'string',
            'description': 'The base query for the search;',
        },
    ...

Just find out that I do not need mixin :) Just docs_fields defined on the view.

Probably this will not fulfill all your needs - but think it's a good start :)

Happy coding!