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
Validation in Django REST framework serializers is handled a little differently to how validation works in Django's ModelForm class. With ModelForm the validation is performed partially on the form, and partially on the model instance. With REST framework the validation is performed entirely on the serializer class.
Django forms submit only if it contains CSRF tokens. It uses uses a clean and easy approach to validate data. The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class. It returns True if data is valid and place all data into a cleaned_data attribute.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With