Search code examples
djangoprimary-keydjango-serializer

Django declaring a read-only field on serializer


I have an endpoint that needs to expose the fields to the user, but under a different name. The user should -not- be able to set the PK (called orderId here) when creating the object, since that should be done automatically with a randomly set value when the object is created.

Here is my serializer.

class OrderSerializer(serializers.ModelSerializer):
    orderId = serializers.UUIDField(source="id")
    orderName = serializers.CharField(source="name")

    class Meta:
        model = Order
        fields = (
            "orderId",
            "orderName",
        )
        read_only_fields = ("orderId",)

Here is my model:

class Order(models.Model):

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=48)

Note that id in the model is exposed to the user under the name orderId.

The problem is this set up seems to make it a required field when when a user creates an order. With the above set up, a POST request setting just orderName in the body to create an order gives this error:

{
    "orderId": [
        "This field is required."
    ]
}

Unless of course the user sends a value for orderId, which they should not do.

I had hoped including the "orderId" as a ready only field would make it not required in a POST request, but that does not seem to be the case. Obviously the field needs to be required in the DB, but it should not be required in a POST request.

If I remove the orderId from fields, I get:

The field 'messageId' was declared on serializer OrderSerializer, but has not been included in the 'fields' option.

And if I remove if from the serialize entirely (remove orderId = serializers.UUIDField(source="id")) then I can create an Order but the id does not show up in a GET request, where I need it to be seen.

How can I make it so that the order's primary key id is...

  • visible to the user in a GET request
  • visible to the user under the name orderId
  • not a required field when generating an order, since it should be automatically created like most Primary Keys are

Solution

  • You should specify read_only=True to mark the field as readonly. By customizing it yourself, you prevented Django from doing this itself:

    class OrderSerializer(serializers.ModelSerializer):
        orderId = serializers.UUIDField(source='id', read_only=True)
        orderName = serializers.CharField(source='name')
    
        class Meta:
            model = Order
            fields = (
                'orderId',
                'orderName',
            )
            read_only_fields = ('orderId',)