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.
framework | version |
djangorestframework | 3.12.4 |
django | 3.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:
- 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)
- 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 theget_queryset
method. The model associated with the queryset is what DRF uses to generate the description, and if it isNone
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 asmy_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.