I'm using a mixin on my viewset so that multiple serializers can be used accross different viewset actions and any custom actions.
I have an extra action called invoice which is just a normal update but using a different serializer. I need to perform an OPTIONS request at the endpoint to get options for a <select> element. The problem is that when I perform the request it's picking up the serializer from the default update - OrderSerializer instead of InvoiceSerializer. How can I pick up the options from the correct serializer?
class MultipleSerializerMixin:
"""
Mixin that allows for multiple serializers based on the view's
`serializer_action_classes` attribute.
ex.
serializer_action_classes = {
'list': ReadOnlyListSerializer,
'retrieve': ReadOnlyDetailSerializer,
}
"""
def get_serializer_class(self):
try:
return self.serializer_action_classes[self.action]
except (KeyError, AttributeError):
return super().get_serializer_class()
class OrderAPIViewSet(MultipleSerializerMixin,
viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = serializers.OrderSerializer
serializer_action_classes = {
'invoice': serializers.InvoiceSerializer,
}
@action(detail=True, methods=['put'], url_name='invoice')
def invoice(self, request, *args, **kwargs):
"""
Invoice the order and order lines.
"""
return self.update(request, *args, **kwargs)
Update:
So after inspecting the determine_actions method in metadata.SimpleMetadata it would seem that when performing an OPTIONS request view.action is metadata instead of invoice which explains why the serializer is defaulting to view.serializer_class.
One workaround is to create an extra action as a schema endpoint that could be accessed via a GET request that manually sets the action to invoice.
@action(detail=True, methods=['get', 'put'])
def invoice_schema(self, request, *args, **kwargs):
self.action = 'invoice'
data = self.metadata_class().determine_metadata(request, self)
return Response(data, status=status.HTTP_200_OK)
A more DRY solution if you have multiple actions that use different serializers would be to override the view's options method and set the action from the query parameters. This could be added to MultipleSerializerMixin to make it the default behaviour for all views that use this mixin.
def options(self, request, *args, **kwargs):
self.action = request.query_params.get('action')
return super().options(request, *args, **kwargs)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With