mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #33613 -- Made createsuperuser detect uniqueness of USERNAME_FIELD when using Meta.constraints.
This commit is contained in:
parent
ae506181f7
commit
13a9cde133
@ -11,6 +11,7 @@ from django.contrib.auth.password_validation import validate_password
|
||||
from django.core import exceptions
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import DEFAULT_DB_ALIAS
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.text import capfirst
|
||||
|
||||
|
||||
@ -277,9 +278,21 @@ class Command(BaseCommand):
|
||||
else "",
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def username_is_unique(self):
|
||||
if self.username_field.unique:
|
||||
return True
|
||||
for unique_constraint in self.UserModel._meta.total_unique_constraints:
|
||||
if (
|
||||
len(unique_constraint.fields) == 1
|
||||
and unique_constraint.fields[0] == self.username_field.name
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _validate_username(self, username, verbose_field_name, database):
|
||||
"""Validate username. If invalid, return a string error message."""
|
||||
if self.username_field.unique:
|
||||
if self.username_is_unique:
|
||||
try:
|
||||
self.UserModel._default_manager.db_manager(database).get_by_natural_key(
|
||||
username
|
||||
|
@ -548,7 +548,7 @@ 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. The field *must* be unique (i.e., have ``unique=True`` set
|
||||
identifier. The field *must* be unique (e.g. have ``unique=True`` set
|
||||
in its definition), unless you use a custom authentication backend that
|
||||
can support non-unique usernames.
|
||||
|
||||
|
@ -11,6 +11,7 @@ from .with_foreign_key import CustomUserWithFK, Email
|
||||
from .with_integer_username import IntegerUsernameUser
|
||||
from .with_last_login_attr import UserWithDisabledLastLoginField
|
||||
from .with_many_to_many import CustomUserWithM2M, CustomUserWithM2MThrough, Organization
|
||||
from .with_unique_constraint import CustomUserWithUniqueConstraint
|
||||
|
||||
__all__ = (
|
||||
"CustomEmailField",
|
||||
@ -20,6 +21,7 @@ __all__ = (
|
||||
"CustomUserWithFK",
|
||||
"CustomUserWithM2M",
|
||||
"CustomUserWithM2MThrough",
|
||||
"CustomUserWithUniqueConstraint",
|
||||
"CustomUserWithoutIsActiveField",
|
||||
"Email",
|
||||
"ExtensionUser",
|
||||
|
22
tests/auth_tests/models/with_unique_constraint.py
Normal file
22
tests/auth_tests/models/with_unique_constraint.py
Normal file
@ -0,0 +1,22 @@
|
||||
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
|
||||
from django.db import models
|
||||
|
||||
|
||||
class CustomUserWithUniqueConstraintManager(BaseUserManager):
|
||||
def create_superuser(self, username, password):
|
||||
user = self.model(username=username)
|
||||
user.set_password(password)
|
||||
user.save(using=self._db)
|
||||
return user
|
||||
|
||||
|
||||
class CustomUserWithUniqueConstraint(AbstractBaseUser):
|
||||
username = models.CharField(max_length=150)
|
||||
|
||||
objects = CustomUserWithUniqueConstraintManager()
|
||||
USERNAME_FIELD = "username"
|
||||
|
||||
class Meta:
|
||||
constraints = [
|
||||
models.UniqueConstraint(fields=["username"], name="unique_custom_username"),
|
||||
]
|
@ -23,6 +23,7 @@ from .models import (
|
||||
CustomUserNonUniqueUsername,
|
||||
CustomUserWithFK,
|
||||
CustomUserWithM2M,
|
||||
CustomUserWithUniqueConstraint,
|
||||
Email,
|
||||
Organization,
|
||||
UserProxy,
|
||||
@ -1065,6 +1066,41 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
|
||||
|
||||
test(self)
|
||||
|
||||
@override_settings(AUTH_USER_MODEL="auth_tests.CustomUserWithUniqueConstraint")
|
||||
def test_existing_username_meta_unique_constraint(self):
|
||||
"""
|
||||
Creation fails if the username already exists and a custom user model
|
||||
has UniqueConstraint.
|
||||
"""
|
||||
user = CustomUserWithUniqueConstraint.objects.create(username="janet")
|
||||
new_io = StringIO()
|
||||
entered_passwords = ["password", "password"]
|
||||
# Enter the existing username first and then a new one.
|
||||
entered_usernames = [user.username, "joe"]
|
||||
|
||||
def return_passwords():
|
||||
return entered_passwords.pop(0)
|
||||
|
||||
def return_usernames():
|
||||
return entered_usernames.pop(0)
|
||||
|
||||
@mock_inputs({"password": return_passwords, "username": return_usernames})
|
||||
def test(self):
|
||||
call_command(
|
||||
"createsuperuser",
|
||||
interactive=True,
|
||||
stdin=MockTTY(),
|
||||
stdout=new_io,
|
||||
stderr=new_io,
|
||||
)
|
||||
self.assertEqual(
|
||||
new_io.getvalue().strip(),
|
||||
"Error: That username is already taken.\n"
|
||||
"Superuser created successfully.",
|
||||
)
|
||||
|
||||
test(self)
|
||||
|
||||
def test_existing_username_non_interactive(self):
|
||||
"""Creation fails if the username already exists."""
|
||||
User.objects.create(username="janet")
|
||||
|
Loading…
Reference in New Issue
Block a user