Skip to content

Commit

Permalink
[1.5.x] Fixed #19822 -- Added validation for uniqueness on USERNAME_F…
Browse files Browse the repository at this point in the history
…IELD on custom User models.

Thanks to Claude Peroz for the draft patch.

(cherry picked from commit f5e4a69)
  • Loading branch information
freakboy3742 committed Feb 15, 2013
1 parent 1c086df commit bc6746a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
22 changes: 22 additions & 0 deletions django/contrib/auth/tests/custom_user.py
Expand Up @@ -144,3 +144,25 @@ class Meta:
app_label = 'auth'

# the is_active attr is provided by AbstractBaseUser


class CustomUserNonUniqueUsername(AbstractBaseUser):
"A user with a non-unique username"
username = models.CharField(max_length=30)

USERNAME_FIELD = 'username'

class Meta:
app_label = 'auth'


class CustomUserBadRequiredFields(AbstractBaseUser):
"A user with a non-unique username"
username = models.CharField(max_length=30, unique=True)
date_of_birth = models.DateField()

USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['username', 'date_of_birth']

class Meta:
app_label = 'auth'
18 changes: 18 additions & 0 deletions django/contrib/auth/tests/management.py
Expand Up @@ -9,6 +9,8 @@
from django.contrib.auth.tests.utils import skipIfCustomUser
from django.core.management import call_command
from django.core.management.base import CommandError
from django.core.management.validation import get_validation_errors
from django.db.models.loading import get_app
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import six
Expand Down Expand Up @@ -170,6 +172,22 @@ def test_swappable_user_missing_required_field(self):
self.assertEqual(CustomUser._default_manager.count(), 0)


class CustomUserModelValidationTestCase(TestCase):
@override_settings(AUTH_USER_MODEL='auth.CustomUserBadRequiredFields')
def test_username_not_in_required_fields(self):
"USERNAME_FIELD should not appear in REQUIRED_FIELDS."
new_io = StringIO()
get_validation_errors(new_io, get_app('auth'))
self.assertIn("The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.", new_io.getvalue())

@override_settings(AUTH_USER_MODEL='auth.CustomUserNonUniqueUsername')
def test_username_non_unique(self):
"A non-unique USERNAME_FIELD should raise a model validation error."
new_io = StringIO()
get_validation_errors(new_io, get_app('auth'))
self.assertIn("The USERNAME_FIELD must be unique. Add unique=True to the field parameters.", new_io.getvalue())


class PermissionDuplicationTestCase(TestCase):

def setUp(self):
Expand Down
6 changes: 5 additions & 1 deletion django/core/management/validation.py
Expand Up @@ -50,12 +50,16 @@ def get_validation_errors(outfile, app=None):
# No need to perform any other validation checks on a swapped model.
continue

# This is the current User model. Check known validation problems with User models
# If this is the current User model, check known validation problems with User models
if settings.AUTH_USER_MODEL == '%s.%s' % (opts.app_label, opts.object_name):
# Check that the USERNAME FIELD isn't included in REQUIRED_FIELDS.
if cls.USERNAME_FIELD in cls.REQUIRED_FIELDS:
e.add(opts, 'The field named as the USERNAME_FIELD should not be included in REQUIRED_FIELDS on a swappable User model.')

# Check that the username field is unique
if not opts.get_field(cls.USERNAME_FIELD).unique:
e.add(opts, 'The USERNAME_FIELD must be unique. Add unique=True to the field parameters.')

# Model isn't swapped; do field-specific validation.
for f in opts.local_fields:
if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
Expand Down
5 changes: 4 additions & 1 deletion docs/topics/auth/customizing.txt
Expand Up @@ -486,7 +486,10 @@ password resets. You must then provide some key implementation details:
A string describing the name of the field on the User model that is
used as the unique identifier. This will usually be a username of
some kind, but it can also be an email address, or any other unique
identifier. In the following example, the field `identifier` is used
identifier. The field *must* be unique (i.e., have ``unique=True``
set in it's definition).

In the following example, the field `identifier` is used
as the identifying field::

class MyUser(AbstractBaseUser):
Expand Down

0 comments on commit bc6746a

Please sign in to comment.