TL;DR
In my Serializer.validate
method, I need to be able to access attrs['field']
and fall back to self.instance.field
if it's not set in the incoming data, and I'm wondering if there is a common pattern to do so.
The Problem
Take the example from the Object-level validation section of the DRF Serializers documentation:
from rest_framework import serializers
class EventSerializer(serializers.Serializer):
description = serializers.CharField(max_length=100)
start = serializers.DateTimeField()
finish = serializers.DateTimeField()
def validate(self, attrs):
"""
Check that start is before finish.
"""
if attrs['start'] < attrs['finish']:
raise serializers.ValidationError("finish must occur after start")
return attrs
(This uses a normal Serializer
, but imagine it's a ModelSerializer
for an Event
model.)
When an event is created, or updated where both the start
and finish
attributes are included in the data, then this serializer works as expected.
However, if you make a request such as:
client.patch(f"/events/{event.id}", {"start": "2021-01-01"})
Then the serializer will fail, because trying to access attrs['finish']
results in a KeyError
. In this case, I need to be able fall back to self.instance.finish
, since the start < finish
validation is still necessary.
Is there a common pattern that solves this problem?
Current Solution
You can solve this by adding a snippet to the start of all validate
methods:
def validate(self, attrs):
full_attrs = attrs
if self.instance is not None:
full_attrs = {
**self.to_internal_value(self.__class__(self.instance).data),
**full_attrs,
}
Then use full_attrs
in place of attrs
. This adds the serialized version of the instance data to attrs
.
Is there a better way to accomplish this?
(The one "downside" with this is that it could prevent otherwise valid updates if the data loses it's integrity. So for instance, if a developer updates the database directly so that event.start
is later than event.finish
, an API user will no longer be able to update event.description
since this validation would fail. But I think the pros definitely outweigh the cons, at least for my current use case.)
question from:
https://stackoverflow.com/questions/65912181/how-to-validate-against-full-model-data-in-django-rest-framework