Search code examples
pythondjangodjango-rest-frameworkswagger-2.0django-rest-swagger

Django Rest Swagger 2: Is there anyway so far to document parameters for POST requests of FUNCTION based views?


I am trying to integrate django-rest-swagger==2.1.1 with my existing project that uses djangorestframework==3.5.3.

The project has some Class based views and some Function based views. After integrating swagger, It displays input boxes for POST requests of "Class Based views"(which have serializers obviously), but does not show for "function based views". The question has been asked several times, I have tried following solutions:

Solution1 Solution2

and few others too, but did not work for my case. Is there any possible way to do that for 'function based views' or I ll have to convert them to class based views?


Solution

  • YAML docstring parser is deprecated in REST Swagger>=2.0
    What I did is to override the SchemaGenerator class to parse the view's docstring by my own conventions.

    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.schemas import SchemaGenerator
    from rest_framework.views import APIView
    
    from rest_framework_swagger import renderers
    
    import yaml
    import coreapi
    import urlparse
    
    class SchemaGenerator(SchemaGenerator):
        def get_link(self, path, method, view):
            """Custom the coreapi using the func.__doc__ .
    
            if __doc__ of the function exist, use the __doc__ building the coreapi. else use the default serializer.
    
            __doc__ in yaml format, eg:
    
            description: the desc of this api.
            parameters:
                - name: mobile
                  desc: the mobile number
                  type: string
                  required: true
                  location: form
                - name: promotion
                  desc: the activity id
                  type: int
                  required: true
                  location: form
            """
            fields = self.get_path_fields(path, method, view)
            yaml_doc = None
            if view and view.__doc__:
                try:
                    yaml_doc = yaml.load(view.__doc__)
                except:
                    yaml_doc = None
    
            if yaml_doc and type(yaml_doc) != str:
                _method_desc = yaml_doc.get('description', '')
                params = yaml_doc.get('parameters', [])
                for i in params:
                    _name = i.get('name')
                    _desc = i.get('description')
                    _required = i.get('required', False)
                    _type = i.get('type', 'string')
                    _location = i.get('location', 'form')
                    field = coreapi.Field(
                        name=_name,
                        location=_location,
                        required=_required,
                        description=_desc,
                        type=_type
                    )
                    fields.append(field)
            else:
                _method_desc = view.__doc__ if view and view.__doc__ else ''
                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
    
            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,
                description=_method_desc
            )
    
    def get_swagger_view(title=None, url=None, patterns=None, urlconf=None):
        """
        Returns schema view which renders Swagger/OpenAPI.
        """
        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 = SchemaGenerator(
                    title=title,
                    url=url,
                    patterns=patterns,
                    urlconf=urlconf
                )
                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()
    

    Create this module anywhere in project structure. Import get_swagger_view from this module in project/urls.py. And, then remove the get_swagger_view method from the django_rest_swagger module.

    Ref: Comment by daimon99 in REST Swagger Issues

    Update: As from django-rest-framework version 3.7, there are breaking changes due to which above code won't work, the solution would be Comment by GuillaumeCisco