6
votes

This is my serializers.py (I want to create a serializer for the built-in User model):

from rest_framework import serializers

from django.contrib.auth.models import User

class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = User
        fields = ('username', 'password', 'email', )

    def validate_username(self, username):
        if not re.search(r'^\w+$', username): #checks if all the characters in username are in the regex. If they aren't, it returns None
            raise serializers.ValidationError('Username can only contain alphanumeric characters and the underscore.')
        try:
            User.objects.get(username=username)
        except ObjectDoesNotExist:
            return username
        raise serializers.ValidationError('Username is already taken.')

The issue is, when I try to create a user using a username which already exists, it returns the following dictionary:

{'username': [u'This field must be unique.']}

rather than saying

{'username': [u'Username is already taken']}

I recreated the validate_username function to this (for testing purposes):

    def validate_username(self, username):
        raise serializers.ValidationError('Testing to see if an error is raised.')

and it doesn't raise an error. Any idea why DjangoRestFramework is ignoring the validate_username function?

Edit: Note that I am using a ModelSerializer (in the tutorial here: http://www.django-rest-framework.org/api-guide/serializers/#validation it talks about field-level validation only for a Serializer, not a ModelSerializer). Note sure if it makes a difference or not.

2
Does it get called appropriately with non alphanumeric characters?jvc26
@jvc26 Nope. It throws an error which says {'username': [u'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.']} The bigger issue I see here is that even when I change the validate_username function to "def validate_username(self, username): raise serializers.ValidationError('Testing to see if the error is raised.')", the error does not even get raised, so it seems as if Django is ignoring my validate_username function (or it just isn't passing to the front-end the raised error which is raised from my validate_username function).SilentDev
@jvc26 I edited my post and noted that I am using a ModelSerializer (not a serializer). Not sure if it makes a difference or not.SilentDev

2 Answers

6
votes

Field-level validation is called before serializer-level validation.

So model User having username as unique=True, the field-level validation will raise exception because of username being already present. DRF's UniqueValidator does this work of raising exception when a field is not unique.

As per DRF source code,

class UniqueValidator:
    """
    Validator that corresponds to `unique=True` on a model field.

    Should be applied to an individual field on the serializer.
    """
    message = _('This field must be unique.')

Since these validators run before serializer-level validation, your validate_username is never called.

1
votes

Try adding the following line in your serializer to do this validator working.

class UserSerializer(serializers.ModelSerializer):
    username = serializers.CharField(max_length=32)

class Meta:
    model = User
    fields = ('username', 'password', 'email', )