22👍
This is how I did it:
class GenreSerializer(serializers.ModelSerializer):
# ... snip ...
def validate_name(self, value):
if self.context['request']._request.method == 'POST':
if self.Meta.model.objects.filter(name=value).exists():
raise ValidationError('A genre with this name already exists.')
return value
In this way the validation is triggered only when a new Genre
object is created (POST
), not when it is updated (PUT
).
When a new Book
object is created, the validation for Genre
is propagated to the nested serializer.
All form inputs are preserved after validation and the error message is attached to the field name
.
That actually fulfills all my criteria. Although I don’t have the feeling that this is the right way of doing it. I’d still like to know how could I call manually the UniqueValidator
in validate_name
, instead of reinventing that validation.
EDIT:
I found out a way how to call the UniqueValidator
in the method:
def validate_name(self, value):
if self.context['request']._request.method == 'POST':
unique = UniqueValidator(
self.Meta.model.objects.all(),
message='Genre with this name already exists.'
)
unique.set_context(self.fields['name'])
unique(value)
return value
EDIT (2022-09-09):
Instead of self.context['request']._request.method
it’s much simpler and cleaner to use self.context['request'].method
.
Tried and tested with Django REST Framework 3.13.
3👍
What I really miss in both solutions is that the validation error is not attached to the field name of the model Genre and the user input in the form is lost.
This is straight forward:
from rest_framework import exceptions
class BookViewSet(viewsets.ModelViewSet):
....
def perform_create(self, serializer):
if check_failed():
raise exceptions.ValidationError(
exceptions._get_error_details({
'genre': {
'name': ['must be unique']
}
})
)
- How to do this join query in Django
- DjangoCMS: disable login via http, force https
- Email verification in Django
- ValueError: "needs to have a value for field "id" before this many-to-many relationship can be used"
- Invert boolean field in update operations with F()
0👍
In addition to @cezar’s answer I was wondering how to not just override based on a condition, but to completely override and ignore any of the fields.
It might be possible to "skip" children serializer validation, or write custom serializer validation to override child serializers, by overriding to_internal_value
of the ParentSerialzer:
class ParentSerializer(serializers.ModelSerializer):
children = ChildSerializer(many=True)
def to_internal_value(self, *args, **kwargs):
try:
# runs the child serializers
return super().to_internal_value(*args, **kwargs)
except ValidationError as e:
# fails, and then overrides the child errors with the parent error
return self.validate(self.initial_data)
def validate(self, attrs):
errors = {}
errors['custom_override_error'] = 'this ignores and overrides the children serializer errors'
if len(errors):
raise ValidationError(errors)
return attrs
class Meta:
model = Parent
- Pylint (pylint_django) does not work when –django-settings-module flag is specified
- How to automatically add target="_blank" to external links only?