Reputation: 6127
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
.
Upvotes: 3
Views: 2663
Reputation: 9
Override get_serializer_class
method is enough and OPTIONS request will detect which serializer to use :
def get_serializer_class(self):
if self.request.method == 'GET':
return ReadOnlyShopSerializer
return ShopSerializer
Upvotes: 0
Reputation: 6127
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)
Upvotes: 2