Search code examples
pythondjangodjango-modelsdjango-rest-frameworkdjango-serializer

How to auto populate a read-only serializer field in django rest framework?


I have a question regarding django rest framework.

Most of the time, I have a serializer which has some read-only fields. For example, consider this simple model below:

class PersonalMessage(models.Model):
    sender = models.ForeignKey(User, related_name="sent_messages", ...)
    recipient = models.ForeignKey(User, related_name="recieved_messages", ...)
    text = models.CharField(...)

    def __str__(self) -> str:
        return f"{self.text} (sender={self.sender})"

In this model, the value of sender and recipient should be automatically provided by the application itself and the user shouldn't be able to edit those fields. Alright, now take a look at this serializer:

class PersonalMessageSerializer(serializers.ModelSerializer):
    class Meta:
        model = PersonalMessage
        fields = '__all__'
        read_only_fields = ('sender', 'recipient')

It perfectly prevents users from setting an arbitrary value on the sender and recipient fields. But the problem is, when these fields are marked as read-only in the serializer, the serializer will completely ignore all the values that are passed into the constructor for these fields. So when I try to create a model, no values would be set for these fields:

PersonalMessageSerializer(data={**request.data, 'sender': ..., 'recipient': ...) # Won't work

What's the best way to prevent users from setting an arbitrary value and at the same time auto-populate those restricted fields in django rest framework?


Solution

  • Depending on how you get those two objects, you can use the serializer's save method to pass them, and they will automatically be applied to the object you are saving:

    sender = User.objects.first()
    recipient = User.objects.last()
    
    serializer = PersonalMessageSerializer(data=request.data)
    message = serializer.save(sender=sender, recipient=recipient)
    

    The kwargs should match the field names in your model for this to work. For reference, have a look here