Suppose I have a view and I need to check that a field is given before calling serializer.save to ensure I don't get a dictionary key error:
class BarView(CreateAPIView):
serializer_class = BarSerializer
queryset = Bar.objects.all()
def perform_create(self, serializer):
if 'foo' not in self.request.data:
raise ParseError('foo field required.')
foo = get_object_or_404(Foo, pk=self.request.data['foo'])
if foo.counter == 10:
raise ParseError('foo limit reached.')
return serializer.save(user=self.request.user, foo=foo)
Instead of returning "foo field required."
I would like to return a message the same as Django REST returns e.g. {"foo":["This field is required."]}
Is there a better way to do this? Perhaps validating the foo field alone with the serializer?
Update: I forgot to mention the user field is also required.
The model for Bar is:
class Bar(models.Model):
user = models.ForeignKey(User, db_index=True, editable=False)
foo = models.ForeignKey(Foo, db_index=True)
Custom exception handlingThe exception handler function should either return a Response object, or return None if the exception cannot be handled. If the handler returns None then the exception will be re-raised and Django will return a standard HTTP 500 'server error' response.
Django Exception Example It raises a DoesNotExist exception if data not found. This is Django's built-in exception. It shows the following exception because no record is available at id 12. We can handle it by using try and except, now let's handle this exception.
non_field_errors () This method returns the list of errors from Form. errors that aren't associated with a particular field. This includes ValidationError s that are raised in Form.
Yes,
Simply look at docs: Validation
(I assumed that field foo
is part of Bar
model, if not please add it to fields
in Meta
):
Add validation to BarSerializer:
class BarSerializer(serializers.ModelSerializer):
def validate_foo(self, value):
if not value:
raise serializers.ValidationError("foo field required.")
if Foo.objects.filter(pk=value, counter__gte=10).exists():
raise serializers.ValidationError("foo limit reached.")
return value
class Meta:
model = Bar
And then create Your View by extending this:
from rest_framework.exceptions import ValidationError
class MyCreateAPIView(CreateAPIView):
def post(self, request, *args, **kwargs):
try:
return super(BarView, self).post(request, *args, **kwargs)
except ValidationError as e:
return Response(e.detail, , status=status.HTTP_400_BAD_REQUEST)
def create(self,request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
try:
self.perform_create(serializer)
except DjangoValidationError as e:
raise ValidationError(e.messages)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(serializer):
# do your stuff
serializer.save()
Yes, the best way to do this is to make the foo
field required in your serializer using extra_kwargs
option in the Meta
class.
DRF will automatically handle the validation for you. You don't need to raise this validation error yourself.
class Meta:
...
extra_kwargs = {'foo': {'required':True}} # make 'foo' a required field.
Now, whenever the foo
field is not passed in the request
, there will be a key foo
in the serializer.errors
dictionary and its value will be This field is required.
Also, create a validate_foo()
function which will validate for the limit of foo_object.counter
.
def validate_foo(self, value):
self.foo_object = get_object_or_404(Foo, pk=value) # get the 'foo' object
if self.foo_object.counter == 10: # check for limits
raise serializers.ValidationError('foo limit reached.') # raise error
return value # must return the value at the end
FINAL CODE:
serializers.py
class BarSerializer(serializers.ModelSerializer):
class Meta:
...
extra_kwargs = {'foo': {'required':True}} # make 'foo' a required field.
def validate_foo(self, value):
self.foo_object = get_object_or_404(Foo, pk=value)
if self.foo_object.counter == 10:
raise serializers.ValidationError('foo limit reached.')
return value
views.py
In your views, you need to override perform_create()
and pass user
and serializer.foo_object
to serializer.save()
function.
class BarView(CreateAPIView):
serializer_class = BarSerializer
queryset = Bar.objects.all()
def perform_create(self, serializer):
return serializer.save(user=self.request.user, foo=serializer.foo_object)
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