Brady Dean
Brady Dean

Reputation: 3573

Remove nesting from ModelSerializer

I'm trying to add routes to GET and PUT the status of a ticket.

class ReturnLabelTicket(models.Model):
   status = models.CharField(choices=...)

class ReturnLabelTicketStatusSerializer(serializers.ModelSerializer):
    """Serializer of a return label ticket status."""

    status = serializers.ChoiceField(ReturnLabelTicket.StatusChoice.choices)

    def to_internal_value(self, data):
        """Take the whole data value as the status."""

        return super().to_internal_value({'status': data})

    def to_representation(self, instance):
        """Return the status string."""

        return instance.status

    def update(self, instance, validated_data):
        """Update the ticket status."""

        instance.status = validated_data.get('status', instance.status)
        instance.save()
        return instance

    class Meta:
        model = ReturnLabelTicket
        fields = ['status']

By default this serializer expects the JSON to be of the form { 'status': '...' }, but I want to remove the status key and use the value directly. My to_internal_value works fine, but when I remove the nested status in to_representation I end up with an error at

ticketing-service    |   File "/opt/app-root/lib64/python3.9/site-packages/rest_framework/serializers.py", line 549, in data
ticketing-service    |     return ReturnDict(ret, serializer=self)
ticketing-service    |   File "/opt/app-root/lib64/python3.9/site-packages/rest_framework/utils/serializer_helpers.py", line 18, in __init__
ticketing-service    |     super().__init__(*args, **kwargs)
ticketing-service    | ValueError: need more than 1 value to unpack

This is the relevant section of my ReturnLabelTicketViewSet.

    @action(
        detail=True,
        url_path='status',
        serializer_class=ReturnLabelTicketStatusSerializer
    )
    def status(self, _request, ticket_id=None):
        """Retrieve the ticket status."""

        ticket = self.get_object()
        serializer = self.get_serializer(ticket)
        return Response(serializer.data)

Upvotes: 0

Views: 94

Answers (1)

mon io
mon io

Reputation: 752

DRF Serializers are designed for django ORM which means that it manipulates django models and primitive dictionaries.

I prefer you to bypass this behaviour directly in status action. You can leave to_representation method in you serializer with native behaviour and edit status action to return only status string (use serializer.instance parameter to get serializer.instance.status string and return it in Response object....

So in short: leave native to_representation method and return Response(serializer.instance.status)

class ReturnLabelTicketStatusSerializer(serializers.ModelSerializer):
    """Serializer of a return label ticket status."""

    status = serializers.ChoiceField(ReturnLabelTicket.StatusChoice.choices)

    def to_internal_value(self, data):
        """Take the whole data value as the status."""

        return super().to_internal_value({'status': data})

    def update(self, instance, validated_data):
        """Update the ticket status."""

        instance.status = validated_data.get('status', instance.status)
        instance.save()
        return instance

    class Meta:
        model = ReturnLabelTicket
        fields = ['status']


@action(
    detail=True,
    url_path='status',
    serializer_class=ReturnLabelTicketStatusSerializer
)
def status(self, _request, ticket_id=None):
    """Retrieve the ticket status."""

    ticket = self.get_object()
    serializer = self.get_serializer(ticket)
    return Response(serializer.instance.status)

Upvotes: 1

Related Questions