Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating OpenAPI Schema in Django Rest Framework: URL Keyword in Update ViewSet

I'm trying to do something that should be pretty straight forward but I'm running into a problem that I can't make heads or tails of. I'm hoping someone can help me sort it out.

I've created some API endpoints using DRF. Naturally, I have Create, Update, Retrieve, and Delete endpoints that I've constructed with the django_rest_framework.generics. I need to send some extra data into the Serializer from the ViewSet on both the CreateAPIView and UpdateAPIView. To accomplish this, I've extended the get_serializer_context() method on those ViewSets.

I'm now trying to create OpenAPI documentation for those said endpoints. However, when I run generateschema in the manage.py it returns an error. This error occurs in each of the UpdateAPIViews where I've extended the get_serializer_context() method. An example of this error is shown below.

AssertionError: Expected view UpdateStaffViewSet to be called with a URL keyword argument named "staff_id". Fix your URL conf, or set the `.lookup_field` attribute on the view correctly.

Naturally, we'd think that I goofed the URL keyword or the lookup_field, just as the error states. Unless I'm looking over something obvious though, I just cannot see what the issue is. These endpoints function perfectly when tested using curl or postman. I do know that if I remove the extended get_serializer_context() method, the generateschema command works fine. (But then an test using postman/curl will fail.) Why in the heck would that extended method matter!?

urls.py

url_patterns = [
    path('v1/staff/retrieve/<int:staff_id>/', RetrieveStaffViewSet.as_view()),
    path('staff/update/<int:staff_id>/', UpdateStaffViewSet.as_view()),
    ...
]

viewsets

class UpdateRetrieveBase:
    serializer_class = StaffSerializer
    queryset = Staff.objects.select_related('user')
    lookup_url_kwarg = 'staff_id'
    lookup_field = 'pk'


class UpdateStaffViewSet(UpdateRetrieveBase, UpdateAPIView):

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['validation_refs'] = get_validation_refs(staff=self.get_object())

        return context


class RetrieveStaffViewSet(UpdateRetrieveBase, RetrieveAPIView):
    pass
like image 418
August Avatar asked Dec 10 '25 01:12

August


1 Answers

Alright well after all that, I found my own answer. I'll post it here in case someone else also gets stumped.

Since the generateschema command runs a test but then doesn't include URL kwargs, we cannot call get_object() in the viewset. The test must account for that but doesn't consider that we may be calling that method anywhere else. To fix this in my particular case I edited the code as thus.

viewset

class UpdateAEAContractViewSet(UpdateRetrieveBase, UpdateAPIView):

    def get_serializer_context(self):
        context = super().get_serializer_context()
        contract_id = self.kwargs.get(self.lookup_url_kwarg)
        context['validation_refs'] = get_validation_refs(contract_id=contract_id)
        return context

validation ref subjob

def get_validation_refs(staff_id=None):
    staff = Staff.objects.get(pk=staff_id) if staff_id else None

    ...
like image 189
August Avatar answered Dec 11 '25 15:12

August



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!