Kevin
Kevin

Reputation: 2277

Django Rest Framework serializer validator always empty

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

Answers (2)

Rahul Gupta
Rahul Gupta

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

demux
demux

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

Related Questions