Reputation: 2277
I am trying to print out the list of validators in django rest framework, however when I print it out it seem like it is always empty.
Here is a part of the code
class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer
def create(self, request):
serializer = self.serializer_class(data=request.data)
# this always return []
print serializer.validators
This is counterintuitive because when I tested invalid data in request.data
, the code clearly runs the validator since is_valid()
return error as the documentation indicates. Therefore it doesn't make sense that I can observe the validator taking effect but can't print it out. Can someone point out what I am missing?
Thanks
Upvotes: 0
Views: 3172
Reputation: 47906
Why serializer.validators
returns []
in your case?
This is happening because you have not passed a validators
arguments to the serializer when creating an instance of the serializer. Had you passed the validators
argument, then serializer._validators
would be set to that value. Then, if you check serializer.validators
, it would give you the list of validators passed.
For Example:
In [1]: serializer = SomeSerializer(data=some_data, validators=[validator1, validator2])
In [2]: serializer.validators
Out [2]: [validator1, validator2] # gives the list of validators
By default, []
is returned if no validators is passed when accessing serializer.validators
.
Source code for reference:
Now, BaseSerializer
inherits from Field
class which calls its __init__()
.
class BaseSerializer(Field):
def __init__(self, instance=None, data=empty, **kwargs):
...
super(BaseSerializer, self).__init__(**kwargs)
Here, default_validators
is an empty list []
.
class Field(object):
default_validators = [] # an empty list by default
default_empty_html = empty
initial = None
def __init__(self, read_only=False, write_only=False,
required=None, default=empty, initial=empty, source=None,
label=None, help_text=None, style=None,
error_messages=None, validators=None, allow_null=False):
...
# Here, it checks if validators argument was passed when creating the serializer instance
# If you have passed, then `.validators` will be set to that value.
if validators is not None:
self.validators = validators[:]
...
@property
def validators(self):
if not hasattr(self, '_validators'):
# This will be executed as there was no validators passed in kwargs
self._validators = self.get_validators()
return self._validators
@validators.setter
def validators(self, validators):
self._validators = validators
def get_validators(self):
return self.default_validators[:] # returns empty list
Why your code shows error for invalid data then?
This is because field-level validators are causing that error. Your serializer-level validators is []
but there are some field-level validators which are raising error for invalid data.
To see field-level validators, you can do print repr(serializer)
.
Upvotes: 3
Reputation: 4654
From http://www.django-rest-framework.org/api-guide/validators/
Validation in REST framework
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. This is advantageous for the following reasons:
- It introduces a proper separation of concerns, making your code behavior more obvious.
- It is easy to switch between using shortcut ModelSerializer classes and using explicit Serializer classes. Any validation behavior being used for ModelSerializer is simple to replicate.
- Printing the
repr
of a serializer instance will show you exactly what validation rules it applies. There's no extra hidden validation behavior being called on the model instance.When you're using ModelSerializer all of this is handled automatically for you. If you want to drop down to using a Serializer classes instead, then you need to define the validation rules explicitly.
Example
As an example of how REST framework uses explicit validation, we'll take a simple model class that has a field with a uniqueness constraint.
class CustomerReportRecord(models.Model): time_raised = models.DateTimeField(default=timezone.now, editable=False) reference = models.CharField(unique=True, max_length=20) description = models.TextField()
Here's a basic ModelSerializer that we can use for creating or updating instances of CustomerReportRecord:
class CustomerReportSerializer(serializers.ModelSerializer): class Meta: model = CustomerReportRecord
If we open up the Django shell using manage.py shell we can now
>>> from project.example.serializers import CustomerReportSerializer >>> serializer = CustomerReportSerializer() >>> print(repr(serializer)) CustomerReportSerializer(): id = IntegerField(label='ID', read_only=True) time_raised = DateTimeField(read_only=True) reference = CharField(max_length=20, validators=[<UniqueValidator(queryset=CustomerReportRecord.objects.all())>]) description = CharField(style={'type': 'textarea'})
Upvotes: 1