Gotchas with djangorestframework and OpenAPI documentation

This is a tale of "I spent a lot of time figuring this out and I'm writing it here so you don't have to."

The information in this post references the following Django and Django Rest Framework versions.

frameworkversion
djangorestframework3.12.4
django3.2.7

I'm building a REST API using Django Rest Framework (DRF), and one of its neat features is that it can generate an OpenAPI schema on the fly, which you can then feed to the likes of Swagger UI to render interactive documentation. Getting something presentable is pretty easy, but fine-tuning it requires a bit of magic.

Individually documenting methods in a ViewSet

There's a lot of ways to manage views. If you're using a class-based view (i.e. a class extending ModelViewSet), you might be wondering how to document individual actions.

There's two ways to do this:

  1. Put a docstring on the action method, i.e.
    class MyViewSet(ModelViewSet):
     def update(self, request, *args, **kwargs):
         """Lorem ipsum dolor set"""
         return Response('{}', status.HTTP_200_OK)
    
  2. Use a specially formatted docstring on the class:
class MyViewSet(ModelViewSet):
    """
    update:
    Lorem ipsum dolor set
    """
    def update(self, request, *args, **kwargs):
        return Response('{}', status.HTTP_200_OK)

The syntax is: action: followed by the action documentation, multi-lines allowed.

Where does action come from? That will typically be in your urlconf, i.e.

path('myview/<int:pk>', views.MyViewSet.as_view({'put': 'update'}), name='myview-update')

Documenting path parameters

Path parameters refer to the REST method parameters that are parsed out of the request URI by the urlconf. For example:

path('/some/path/<int:pk>', MyViewSet.as_view({'get':'retrieve'})

This will pass pk to the retrieve() method via kwargs.

The question is, how do you document what pk is supposed to be?

The OpenAPI generator attempts to find this information via your model--more specifically, the model associated with the queryset attached to the view. If you set the help_text attribute on your model fields, it will get automatically pulled into the generated OpenAPI schema.

Sometimes.

There's some magic behavior involved here, and it's really easy to break it.

"I set the help_text attribute on my model but it's not showing up! Why isn't it working?"

DRF requires all of the following to be true:

  • You must have the queryset property set on your View/ViewSet class, even if you've overridden the get_queryset method. The model associated with the queryset is what DRF uses to generate the description, and if it is None then you get a default string.
  • The path parameter name must match the model field name. You can't have a field named name but a path parameter defined as my_id.

With the case of foreign keys, I think there's some leeway where either foo or foo_id will work.

These are the gotchas I have run into so far. I hope this helps someone.