django-rest-swagger: How can I specify the parameter type in the docstring

Question:

I am using django-rest-framwork and django-rest-swagger.

The problem is that I’m fetching data directly from the body of the request:

def put(self, request, format=None):
    """                                                                                                                                                                                                
    This text is the description for this API                                                                                                                                                          
    username -- username                                                                                                                                                                               
    password -- password                                                                                                                                                                               
    """
    username = request.DATA['username']
    password = request.DATA['password']

but when I try the request from the swagger-ui I can’t specify the “parameter type” (it’s by default query and can’t find a way to change it from the docstring)

I have managed to get around my problem by changing some line in the function build_query_params_from_docstring from the file “introspectors.py” but I was wondering if there is another way to do it.

Asked By: Alexis

||

Answers:

UPDATE: This answer only works for django-rest-swagger < 2, see the comment from @krd below.

The docs: http://django-rest-swagger.readthedocs.org/en/latest/yaml.html

If you want to put form-data:

def put(self, request, format=None):
    """
    This text is the description for this API.

    ---
    parameters:
    - name: username
      description: Foobar long description goes here
      required: true
      type: string
      paramType: form
    - name: password
      paramType: form
      required: true
      type: string
    """
    username = request.DATA['username']
    password = request.DATA['password']

For a JSON body you can do something like:

def put(...):
    """
    ...

    ---
    parameters:
    - name: body
      description: JSON object containing two strings: password and username.
      required: true
      paramType: body
      pytype: RequestSerializer
    """
    ...
Answered By: zvyn

Define a filter-class in your viewset. django-rest does not do this yaml stuff for parameters anymore. The fields you define in your filterclass will appear as fields in your openapi / swagger documentation. This is very neat.

READ full documentation.

http://www.django-rest-framework.org/apiguide/filtering/#djangofilterbackend

from django_filters.rest_framework.filterset import FilterSet

class ProductFilter(FilterSet):

    class Meta(object):
        models = models.Product
        fields = (
            'name', 'category', 'id', )


class PurchasedProductsList(generics.ListAPIView):
    """
    Return a list of all the products that the authenticated
    user has ever purchased, with optional filtering.
    """
    model = Product
    serializer_class = ProductSerializer
    filter_class = ProductFilter

    def get_queryset(self):
        user = self.request.user
        return user.purchase_set.all()

the fields defined in the filterseet will show up in de documentation.
but there will be no description.

Answered By: Stephan

The only way I’ve had success in defining parameter types is by creating a view that defines what I want without using the generator.

class SwaggerSchemaView(APIView):
permission_classes = [IsAuthenticatedOrReadOnly,]
renderer_classes = [renderers.OpenAPIRenderer, renderers.SwaggerUIRenderer]

schema = coreapi.Document(
    title='Thingy API thing',
        'range': coreapi.Link(
            url='/range/{start}/{end}',
            action='get',
            fields=[
                coreapi.Field(
                    name='start',
                    required=True,
                    location='path',
                    description='start time as an epoch',
                    type='integer'
                ),
                coreapi.Field(
                    name='end',
                    required=True,
                    location='path',
                    description='end time as an epoch',
                    type='integer'
                )
            ],
            description='show the things between the things'
        ),
    }
)

and then using that class in urls.py

urlpatterns = [
    url(r'^$', SwaggerSchemaView.as_view()),
    ...
]
Answered By: John VanBuskirk

Similar to John VanBuskirk’s answer, here is what I have:

The actual manual created doc:

drf_api/business/schema.py

# encoding: utf-8
from __future__ import unicode_literals
from __future__ import absolute_import
import coreapi

schema = coreapi.Document(
    title='Business Search API',
    url='/api/v3/business/',
    content={
        'search': coreapi.Link(
            url='/',
            action='get',
            fields=[
                coreapi.Field(
                    name='what',
                    required=True,
                    location='query',
                    description='Search term'
                ),
                coreapi.Field(
                    name='where',
                    required=True,
                    location='query',
                    description='Search location'
                ),
            ],
            description='Search business listings'
        )
    }
)

Then copied the get_swagger_view function and customized it:

drf_api/swagger.py

# encoding: utf-8
from __future__ import unicode_literals
from __future__ import absolute_import
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 django.utils.module_loading import import_string


def get_swagger_view(schema_location):
    """
    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):
            schema = None

            try:
                schema = import_string(schema_location)
            except:
                pass

            if not schema:
                raise exceptions.ValidationError(
                    'The schema generator did not return a schema Document'
                )

            return Response(schema)


    return SwaggerSchemaView.as_view()

Then hook it up to the urls.py

from ..swagger import get_swagger_view
from . import views

schema_view = get_swagger_view(schema_location='drf_api.business.schema.schema')

urlpatterns = [
    url(r'^swagger/$', schema_view),
Answered By: James Lin

For latest django-rest-framework > 3.7 and django-rest-swagger > 2 , go through the below link to find working solution

https://github.com/marcgibbons/django-rest-swagger/issues/549#issuecomment-371860030

Answered By: Lokesh Sanapalli

For Django Rest Framework >= 2.0

I am using a serializer and apply to a the view function with a decorator:

from types import MethodType
from typing import Optional, List, Callable, Any

from rest_framework.decorators import api_view as drf_api_view
from rest_framework.serializers import BaseSerializer

Function = Callable[..., Any]


def api_view(
    http_method_names: Optional[List[str]] = None,
    use_serializer: Optional[BaseSerializer] = None
) -> Function:
    if use_serializer is None:
        return drf_api_view(http_method_names)

    def api_view_deco_wrap(view: Function) -> Function:
        nonlocal http_method_names, use_serializer

        decorated_view = drf_api_view(http_method_names)(view)

        if use_serializer:
            decorated_view.cls.get_serializer = 
                MethodType(lambda s: use_serializer(), decorated_view.cls)

        return decorated_view

    return api_view_deco_wrap

Then in the function I use:

@util.api_view(["POST"], use_serializer=serializers.MySerializer)
def replace(request, pk):
    pass

And works!!!

Answered By: Felipe Buccioni

[enter image description here][1]For me it worked with the ManualSchema. You have to define the description and fields. See:

from rest_framework import views
import coreapi
import coreschema
from rest_framework.schemas import ManualSchema

class RegisterView(views.APIView):

    schema = ManualSchema(
        description="User register endpoint.",
        fields=[
            coreapi.Field(
                'username',
                required=True,
                location='path',
                description='A unique username',
                schema=coreschema.String(),
                ),
            coreapi.Field(
                'password',
                required=True,
                location='path',
                schema=coreschema.String(),
                ),
            ]
        )

    )
    ...


Answered By: milenakowalska
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.