How to validate a field on update in DRF?
Question:
I have a serializer for a model with a foreign key. The requirement is that on create, foreign key can be set to any existing object from the related model, but on update the related object cannot be changed. I can check this in the custom update()
, but it would be more elegant to use serializer validation to check for this? But I am not sure how. Example code:
class Person(models.Model):
name = models.CharField(max_length=256)
spouse = models.ForeignKey(Person)
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
# this is how I know how to do this
def create(self, validated_data):
try:
spouse = Person.objects.get(pk=int(validated_data.pop('spouse')))
except Person.DoesNotExist:
raise ValidationError('Imaginary spouses not allowed!')
return Person.objects.create(spouse=spouse, **validation_data)
def update(self, person, validated_data):
if person.spouse.pk != int(validated_data['spouse']):
raise ValidationError('Till death do us part!')
person.name = validation_data.get('name', person.name)
person.save()
return person
# the way I want to do this
def validate_spouse(self, value):
# do validation magic
Answers:
You can definitely do this using the validation on a field. The way you’d check if it’s an update vs. creation is checking for self.instance
in the validation function. There’s a bit mentioned about it in the serializer documentation.
self.instance
will hold the existing object and it’s values, so you can then use it to compare against.
I believe this should work for your purposes:
def validate_spouse(self, value):
if self.instance and value != self.instance.spouse:
raise serializers.ValidationError("Till death do us part!")
return value
Another way to do this is to override if the field is read_only if you’re updating. This can be done in the __init__
of the serializer. Similar to the validator, you’d simply look for an instance and if there’s data:
def __init__(self, *args, **kwargs):
# Check if we're updating.
updating = "instance" in kwargs and "data" in kwargs
# Make sure the original initialization is done first.
super().__init__(*args, **kwargs)
# If we're updating, make the spouse field read only.
if updating:
self.fields['spouse'].read_only = True
I have a serializer for a model with a foreign key. The requirement is that on create, foreign key can be set to any existing object from the related model, but on update the related object cannot be changed. I can check this in the custom update()
, but it would be more elegant to use serializer validation to check for this? But I am not sure how. Example code:
class Person(models.Model):
name = models.CharField(max_length=256)
spouse = models.ForeignKey(Person)
class PersonSerializer(serializers.ModelSerializer):
class Meta:
model = Person
# this is how I know how to do this
def create(self, validated_data):
try:
spouse = Person.objects.get(pk=int(validated_data.pop('spouse')))
except Person.DoesNotExist:
raise ValidationError('Imaginary spouses not allowed!')
return Person.objects.create(spouse=spouse, **validation_data)
def update(self, person, validated_data):
if person.spouse.pk != int(validated_data['spouse']):
raise ValidationError('Till death do us part!')
person.name = validation_data.get('name', person.name)
person.save()
return person
# the way I want to do this
def validate_spouse(self, value):
# do validation magic
You can definitely do this using the validation on a field. The way you’d check if it’s an update vs. creation is checking for self.instance
in the validation function. There’s a bit mentioned about it in the serializer documentation.
self.instance
will hold the existing object and it’s values, so you can then use it to compare against.
I believe this should work for your purposes:
def validate_spouse(self, value):
if self.instance and value != self.instance.spouse:
raise serializers.ValidationError("Till death do us part!")
return value
Another way to do this is to override if the field is read_only if you’re updating. This can be done in the __init__
of the serializer. Similar to the validator, you’d simply look for an instance and if there’s data:
def __init__(self, *args, **kwargs):
# Check if we're updating.
updating = "instance" in kwargs and "data" in kwargs
# Make sure the original initialization is done first.
super().__init__(*args, **kwargs)
# If we're updating, make the spouse field read only.
if updating:
self.fields['spouse'].read_only = True