Fixed #19822 -- Added validation for uniqueness on USERNAME_FIELD on custom User models.
Thanks to Claude Peroz for the draft patch.
This commit is contained in:
parent
f179a5198e
commit
f5e4a699ca
|
@ -144,3 +144,25 @@ class IsActiveTestUser1(AbstractBaseUser):
|
||||||
app_label = 'auth'
|
app_label = 'auth'
|
||||||
|
|
||||||
# the is_active attr is provided by AbstractBaseUser
|
# 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'
|
||||||
|
|
|
@ -9,6 +9,8 @@ from django.contrib.auth.tests import CustomUser
|
||||||
from django.contrib.auth.tests.utils import skipIfCustomUser
|
from django.contrib.auth.tests.utils import skipIfCustomUser
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.core.management.base import CommandError
|
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 import TestCase
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
from django.utils import six
|
from django.utils import six
|
||||||
|
@ -170,6 +172,22 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||||
self.assertEqual(CustomUser._default_manager.count(), 0)
|
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):
|
class PermissionDuplicationTestCase(TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -49,12 +49,16 @@ def get_validation_errors(outfile, app=None):
|
||||||
# No need to perform any other validation checks on a swapped model.
|
# No need to perform any other validation checks on a swapped model.
|
||||||
continue
|
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):
|
if settings.AUTH_USER_MODEL == '%s.%s' % (opts.app_label, opts.object_name):
|
||||||
# Check that the USERNAME FIELD isn't included in REQUIRED_FIELDS.
|
# Check that the USERNAME FIELD isn't included in REQUIRED_FIELDS.
|
||||||
if cls.USERNAME_FIELD in cls.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.')
|
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.
|
# Model isn't swapped; do field-specific validation.
|
||||||
for f in opts.local_fields:
|
for f in opts.local_fields:
|
||||||
if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
|
if f.name == 'id' and not f.primary_key and opts.pk.name == 'id':
|
||||||
|
|
|
@ -492,7 +492,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
|
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
|
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
|
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::
|
as the identifying field::
|
||||||
|
|
||||||
class MyUser(AbstractBaseUser):
|
class MyUser(AbstractBaseUser):
|
||||||
|
|
Loading…
Reference in New Issue