In Django Rest Framework (and Django), traditionally we check fields in validate_<field> method, and make more global checks in validate method.
However, look at this code snippet:
def validate(self, data):
# ....
try:
customer.activate(data['signup_code'], data['raw_password'])
except BadCodeProvided:
raise ValidationError(MSG_WRONG_ACTIVATION_CODE)
except SomeOtherException:
raise ValidationError(SOME_OTHER_MESSAGE)
Here, I'm forced to use validatemethod because I'm using 2 fields for my validation (signup_code and raw_password).
However, if an error occurs in a BadCodeProvided Exception, I know it's related to the signup_code field (and not the raw_password one) because of the exception raised here.
In the snippet code above, thiw will create a "non_field_error".
Question: is there a way in DRF to raise the same error but related to the "signup_code" field? (like it would be done in a validate_signup_code method).
Thanks
you can use serializers.ValidationError in serializer :
raise serializers.ValidationError({"myField": "custom message error 1",
"myField2": "custom message error 1"})
doc here Validator DRF
Related
I am trying to create an update Serializer and return a message for Exception IntegrityError.
snippet of the particular lines of codes is below-
instance.username = validated_data.get('username', instance.username)
if User.objects.filter(username__iexact=instance.username).exists():
raise BaseException
How can i return a message which says username already exists without using Try and Except
update-
This is how i fixed this problem-
if User.objects.filter(username__iexact=instance.username).exists():
raise serializers.ValidationError("username already exists")
User.objects.filter(username__iexact=instance.username).exists()
This won't work because the instance is also User object so it will always return True, you need to exclude the current instance first.
Fixed query:
User.objects.exclude(pk=instance.pk).filter(username__iexact=instance.username).exists()
I don't know why you don't want to use try/catch but anyway, here's what you could do while following good patterns:
Django's User model already has the unique=True constraint.
Model instances that violate the UNIQUE constraint already throw an IntegrityError on call to save().
So, in your view you just need to catch for the IntegrityError and then raise serializers.ValidationError.
from rest_framework imports serializers, viewsets
from rest_framework.exceptions ValidationError
class UserProfileUpdateViewSet(viewsets.ModelViewSet):
def perform_update(self, serializer):
"""
Overwriting the method
"""
try:
serializer.save()
except IntegrityError:
raise ValidationError(
f"Username already taken. Please choose another one.",
)
Is there a way to validate params that are passed with the request without writing boilerplate code? Now I've got something like this:
project_id = kwargs['project_id']
try:
project_obj = Project.objects.get(id=project_id)
except Project.DoesNotExist:
return Response(
{'message': 'Requested project does not exist'},
status=status.HTTP_404_NOT_FOUND
)
except ValueError:
return Response(
{'message': 'Project id must be a number'},
status=status.HTTP_400_BAD_REQUEST
)
I've read about Serializers' Validation but I'm not sure it's the right thing. Without handling these exceptions, Django just returns 500, it's not the behavior I actually want.
You are going to wind up writing some boilerplate, but you can clean what you have up by specifying relevant field types like using an IntegerField would spare you checking the type and get_object_or_404 would spare you the try/ catch in those cases.
However, you really want to use the real right field for this job and it's going to be a Relation field, most likely PrimaryKeyRelatedField.
I am new to Django and rest_framework. I have a password complexity rules script for 'new user' page.
If my complexity requirements satisfy the needs, it will return true. Else it raises serialiser.ValidationError.
I duplicated Django's forget password mechanism to apply my password rules.
When it raises an error, application crashes like below.
Exception Type: ValidationError
Exception Value:
[u"The two password fields didn't match."]
Is it possible to use serializer errors as form errors {{ form.new_password1.errors }}?
It's possible to write your own custom exception handler to return the error in a preferred format.
The official documentation, at the bottom of the page, says:
The ValidationError class should be used for serializer and field validation, and by validator classes. It is also raised when calling
serializer.is_valid with the raise_exception keyword argument:
The generic views use the raise_exception=True flag, which means that you can override the style of validation error responses globally
in your API. To do so, use a custom exception handler, as described
above.
serializer.is_valid(raise_exception=True)
By default this exception results in a response with the HTTP status
code "400 Bad Request"
Please read here to create your custom handler.
I'm developing a REST API which takes POST requests from some really brain-dead software which can't PATCH or anything else. The POSTs are to update Model objects which already exist in the database.
Specifically, I'm POSTing data for objects with a related field (a SlugRelatedField, as the POSTer knows the 'name' attribute but NOT the 'pk'). However, I need to return a 404 if the POSTer sends data where the 'name' returns nothing on the SlugRelatedField (e.g. the related object does not exist). I've been through this with a debugger but it seems that DRF uses some Django signals magic to do it The Way DRF Does Itâ„¢, which is to return a 400 BAD REQUEST. I don't know how to modify this - only when it's the above condition and not a true 400-worthy POST - into a 404.
By the way, pre_save() in my view is NOT executing during execution of the failing test.
Here's the serializer:
class CharacterizationSerializer(serializers.ModelSerializer):
"""
Work-in-progress for django-rest-framework use. This handles (de)serialization
of data into a Characterization object and vice versa.
See: http://www.django-rest-framework.org/tutorial/1-serialization
"""
creator = serializers.Field(source='owner.user.username')
sample = serializers.SlugRelatedField(slug_field='name',
required=True,
many=False,
read_only=False)
class Meta:
model = Characterization
# leaving 'request' out because it's been decided to deprecate it. (...maybe?)
fields = ('sample', 'date', 'creator', 'comments', 'star_volume', 'solvent_volume',
'solution_center', 'solution_var', 'solution_minimum', 'solution_min_stddev',
'solution_test_len',)
And here's the view where pre_save isn't being run in the given test (but does get run in some others):
class CharacterizationList(generics.ListCreateAPIView):
queryset = Characterization.objects.all()
serializer_class = CharacterizationSerializer
permission_classes = (AnonPostAllowed,) # #todo XXX hack for braindead POSTer
def pre_save(self, obj):
# user isn't sent as part of the serialized representation,
# but is instead a property of the incoming request.
if not self.request.user.is_authenticated():
obj.owner = get_dummy_proxyuser() # this is done for CharacterizationList so unauthed users can POST. #todo XXX hack
else:
obj.owner = ProxyUser.objects.get(pk=self.request.user.pk)
# here, we're fed a string sample name, but we need to look up
# the actual sample model.
# #TODO: Are we failing properly if it doesn't exist? Should
# throw 404, not 400 or 5xx.
# except, this code doesn't seem to be run directly when debugging.
# a 400 is thrown; DRF must be bombing out before pre_save?
obj.sample = Sample.objects.get(name=self.request.DATA['sample'])
And for good measure, here's the failing test:
def test_bad_post_single_missing_sample(self):
url = reverse(self._POST_ONE_VIEW_NAME)
my_sample_postdict = self.dummy_plqy_postdict.copy()
my_sample_postdict["sample"] = "I_DONT_EXIST_LUL"
response = self.rest_client.post(url, my_sample_postdict)
self.assertTrue(response.status_code == 404,
"Expected 404 status code, got %d (%s). Content: %s" % (response.status_code, response.reason_phrase, response.content))
If I put a breakpoint in at the self.rest_client.post() call, the response already has a 400 in it at that point.
You can use a Django Shortcut for that, getting the obj.sample:
from django.shortcuts import get_object_or_404
obj.sample = get_object_or_404(Sample, name=self.request.DATA['sample'])
Instead of using pre_save why not override post in your API view:
def post(self, request, *args, **kwargs):
...other stuff
try:
obj.sample = Sample.objects.get(name=self.request.DATA['sample'])
...or whatever other tests you want to do
except:
return Response(status=status.HTTP_404_NOT_FOUND)
response = super(CharacterizationList, self).post(request, *args, **kwargs)
return response
Make sure you import DRF's status:
from rest_framework import status
Also, note you will likely want to be more specific with the Exceptions you catch. Django's get method will return either DoesNotExist if nothing matches or MultipleObjectsReturned if more than one object matches. The relevant documentation:
Note that there is a difference between using get(), and using
filter() with a slice of [0]. If there are no results that match the
query, get() will raise a DoesNotExist exception. This exception is an
attribute of the model class that the query is being performed on - so
in the code above, if there is no Entry object with a primary key of
1, Django will raise Entry.DoesNotExist.
Similarly, Django will complain if more than one item matches the
get() query. In this case, it will raise MultipleObjectsReturned,
which again is an attribute of the model class itself.
I'm using Django 1.2.4. I have a model that has a field that needs to be validated. When validation fails, I'd like to display a custom error message to the user. Model editing is done in the admin interface.
This is what I'm doing currently:
def clean_fields(self, exclude=None):
# do validation
if problem:
raise ValidationError({'field_name': "error message"})
Unfortunately, all this does is print out a separate validation message on the admin page for each character in the value of field_name.
What is the proper way to signal the error message I want?
Without looking, it sounds like the admin is looking for an iterable as the value for field_name. Try:
raise ValidationError({'field_name': ["error message",]})
I think the admin expects any number of validation messages to be associated with each field on a form.