Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #19067 -- Clarified handling of username in createsuperuser.

Thanks to clelland for the report, and Preston Holmes for the draft patch.
  • Loading branch information...
commit b3b3db3d954a5226f870a0b4403343c78efae8dc 1 parent c433fcb
Russell Keith-Magee freakboy3742 authored
90 django/contrib/auth/management/commands/createsuperuser.py
View
@@ -16,50 +16,56 @@
class Command(BaseCommand):
- option_list = BaseCommand.option_list + (
- make_option('--username', dest='username', default=None,
- help='Specifies the username for the superuser.'),
- make_option('--noinput', action='store_false', dest='interactive', default=True,
- help=('Tells Django to NOT prompt the user for input of any kind. '
- 'You must use --username with --noinput, along with an option for '
- 'any other required field. Superusers created with --noinput will '
- ' not be able to log in until they\'re given a valid password.')),
- make_option('--database', action='store', dest='database',
- default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
- ) + tuple(
- make_option('--%s' % field, dest=field, default=None,
- help='Specifies the %s for the superuser.' % field)
- for field in get_user_model().REQUIRED_FIELDS
- )
+ def __init__(self, *args, **kwargs):
+ # Options are defined in an __init__ method to support swapping out
+ # custom user models in tests.
+ super(Command, self).__init__(*args, **kwargs)
+ self.UserModel = get_user_model()
+ self.username_field = self.UserModel._meta.get_field(self.UserModel.USERNAME_FIELD)
+
+ self.option_list = BaseCommand.option_list + (
+ make_option('--%s' % self.UserModel.USERNAME_FIELD, dest=self.UserModel.USERNAME_FIELD, default=None,
+ help='Specifies the login for the superuser.'),
+ make_option('--noinput', action='store_false', dest='interactive', default=True,
+ help=('Tells Django to NOT prompt the user for input of any kind. '
+ 'You must use --%s with --noinput, along with an option for '
+ 'any other required field. Superusers created with --noinput will '
+ ' not be able to log in until they\'re given a valid password.' %
+ self.UserModel.USERNAME_FIELD)),
+ make_option('--database', action='store', dest='database',
+ default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
+ ) + tuple(
+ make_option('--%s' % field, dest=field, default=None,
+ help='Specifies the %s for the superuser.' % field)
+ for field in self.UserModel.REQUIRED_FIELDS
+ )
+
+ option_list = BaseCommand.option_list
help = 'Used to create a superuser.'
def handle(self, *args, **options):
- username = options.get('username', None)
+ username = options.get(self.UserModel.USERNAME_FIELD, None)
interactive = options.get('interactive')
verbosity = int(options.get('verbosity', 1))
database = options.get('database')
- UserModel = get_user_model()
-
- username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
- other_fields = UserModel.REQUIRED_FIELDS
-
# If not provided, create the user with an unusable password
password = None
- other_data = {}
+ user_data = {}
# Do quick and dirty validation if --noinput
if not interactive:
try:
if not username:
- raise CommandError("You must use --username with --noinput.")
- username = username_field.clean(username, None)
+ raise CommandError("You must use --%s with --noinput." %
+ self.UserModel.USERNAME_FIELD)
+ username = self.username_field.clean(username, None)
- for field_name in other_fields:
+ for field_name in self.UserModel.REQUIRED_FIELDS:
if options.get(field_name):
- field = UserModel._meta.get_field(field_name)
- other_data[field_name] = field.clean(options[field_name], None)
+ field = self.UserModel._meta.get_field(field_name)
+ user_data[field_name] = field.clean(options[field_name], None)
else:
raise CommandError("You must use --%s with --noinput." % field_name)
except exceptions.ValidationError as e:
@@ -74,9 +80,8 @@ def handle(self, *args, **options):
# Get a username
while username is None:
- username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
if not username:
- input_msg = capfirst(username_field.verbose_name)
+ input_msg = capfirst(self.username_field.verbose_name)
if default_username:
input_msg += " (leave blank to use '%s')" % default_username
raw_value = input(input_msg + ': ')
@@ -84,31 +89,30 @@ def handle(self, *args, **options):
if default_username and raw_value == '':
raw_value = default_username
try:
- username = username_field.clean(raw_value, None)
+ username = self.username_field.clean(raw_value, None)
except exceptions.ValidationError as e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
username = None
continue
try:
- UserModel.objects.using(database).get(**{
- UserModel.USERNAME_FIELD: username
- })
- except UserModel.DoesNotExist:
+ self.UserModel.objects.db_manager(database).get_by_natural_key(username)
+ except self.UserModel.DoesNotExist:
pass
else:
- self.stderr.write("Error: That username is already taken.")
+ self.stderr.write("Error: That %s is already taken." %
+ self.username_field.verbose_name)
username = None
- for field_name in other_fields:
- field = UserModel._meta.get_field(field_name)
- other_data[field_name] = options.get(field_name)
- while other_data[field_name] is None:
+ for field_name in self.UserModel.REQUIRED_FIELDS:
+ field = self.UserModel._meta.get_field(field_name)
+ user_data[field_name] = options.get(field_name)
+ while user_data[field_name] is None:
raw_value = input(capfirst(field.verbose_name + ': '))
try:
- other_data[field_name] = field.clean(raw_value, None)
+ user_data[field_name] = field.clean(raw_value, None)
except exceptions.ValidationError as e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
- other_data[field_name] = None
+ user_data[field_name] = None
# Get a password
while password is None:
@@ -128,6 +132,8 @@ def handle(self, *args, **options):
self.stderr.write("\nOperation cancelled.")
sys.exit(1)
- UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
+ user_data[self.UserModel.USERNAME_FIELD] = username
+ user_data['password'] = password
+ self.UserModel.objects.db_manager(database).create_superuser(**user_data)
if verbosity >= 1:
self.stdout.write("Superuser created successfully.")
4 django/contrib/auth/tests/custom_user.py
View
@@ -23,8 +23,8 @@ def create_user(self, email, date_of_birth, password=None):
user.save(using=self._db)
return user
- def create_superuser(self, username, password, date_of_birth):
- u = self.create_user(username, password=password, date_of_birth=date_of_birth)
+ def create_superuser(self, email, password, date_of_birth):
+ u = self.create_user(email, password=password, date_of_birth=date_of_birth)
u.is_admin = True
u.save(using=self._db)
return u
2  django/contrib/auth/tests/management.py
View
@@ -138,7 +138,7 @@ def test_swappable_user(self):
new_io = StringIO()
call_command("createsuperuser",
interactive=False,
- username="joe@somewhere.org",
+ email="joe@somewhere.org",
date_of_birth="1976-04-01",
stdout=new_io,
skip_validation=True
110 docs/topics/auth.txt
View
@@ -1878,47 +1878,54 @@ The easiest way to construct a compliant custom User model is to inherit from
implementation of a `User` model, including hashed passwords and tokenized
password resets. You must then provide some key implementation details:
-.. attribute:: User.USERNAME_FIELD
+.. class:: models.CustomUser
- 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
- as the identifying field::
+ .. attribute:: User.USERNAME_FIELD
- class MyUser(AbstractBaseUser):
- identfier = models.CharField(max_length=40, unique=True, db_index=True)
- ...
- USERNAME_FIELD = 'identifier'
+ 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
+ as the identifying field::
-.. attribute:: User.REQUIRED_FIELDS
+ class MyUser(AbstractBaseUser):
+ identfier = models.CharField(max_length=40, unique=True, db_index=True)
+ ...
+ USERNAME_FIELD = 'identifier'
- A list of the field names that *must* be provided when creating
- a user. For example, here is the partial definition for a User model
- that defines two required fields - a date of birth and height::
+ .. attribute:: User.REQUIRED_FIELDS
- class MyUser(AbstractBaseUser):
- ...
- date_of_birth = models.DateField()
- height = models.FloatField()
- ...
- REQUIRED_FIELDS = ['date_of_birth', 'height']
+ A list of the field names that *must* be provided when creating
+ a user. For example, here is the partial definition for a User model
+ that defines two required fields - a date of birth and height::
+
+ class MyUser(AbstractBaseUser):
+ ...
+ date_of_birth = models.DateField()
+ height = models.FloatField()
+ ...
+ REQUIRED_FIELDS = ['date_of_birth', 'height']
-.. method:: User.get_full_name():
+ .. note::
- A longer formal identifier for the user. A common interpretation
- would be the full name name of the user, but it can be any string that
- identifies the user.
+ ``REQUIRED_FIELDS`` must contain all required fields on your User
+ model, but should *not* contain the ``USERNAME_FIELD``.
-.. method:: User.get_short_name():
+ .. method:: User.get_full_name():
- A short, informal identifier for the user. A common interpretation
- would be the first name of the user, but it can be any string that
- identifies the user in an informal way. It may also return the same
- value as :meth:`django.contrib.auth.User.get_full_name()`.
+ A longer formal identifier for the user. A common interpretation
+ would be the full name name of the user, but it can be any string that
+ identifies the user.
+
+ .. method:: User.get_short_name():
+
+ A short, informal identifier for the user. A common interpretation
+ would be the first name of the user, but it can be any string that
+ identifies the user in an informal way. It may also return the same
+ value as :meth:`django.contrib.auth.User.get_full_name()`.
The following methods are available on any subclass of
-:class:`~django.contrib.auth.models.AbstractBaseUser`::
+:class:`~django.contrib.auth.models.AbstractBaseUser`:
.. class:: models.AbstractBaseUser
@@ -1979,33 +1986,36 @@ defines different fields, you will need to define a custom manager that
extends :class:`~django.contrib.auth.models.BaseUserManager` providing two
additional methods:
-.. method:: UserManager.create_user(username, password=None, **other_fields)
+.. class:: models.CustomUserManager
- The prototype of `create_user()` should accept all required fields
- as arguments. For example, if your user model defines `username`,
- and `date_of_birth` as required fields, then create_user should be
- defined as::
+ .. method:: models.CustomUserManager.create_user(*username_field*, password=None, **other_fields)
- def create_user(self, username, date_of_birth, password=None):
- # create user here
+ The prototype of `create_user()` should accept the username field,
+ plus all required fields as arguments. For example, if your user model
+ uses `email` as the username field, and has `date_of_birth` as a required
+ fields, then create_user should be defined as::
-.. method:: UserManager.create_superuser(username, password, **other_fields)
+ def create_user(self, email, date_of_birth, password=None):
+ # create user here
- The prototype of `create_superuser()` should accept all required fields
- as arguments. For example, if your user model defines `username`,
- and `date_of_birth` as required fields, then create_user should be
- defined as::
+ .. method:: models.CustomUserManager.create_superuser(*username_field*, password, **other_fields)
- def create_superuser(self, username, date_of_birth, password):
- # create superuser here
+ The prototype of `create_user()` should accept the username field,
+ plus all required fields as arguments. For example, if your user model
+ uses `email` as the username field, and has `date_of_birth` as a required
+ fields, then create_superuser should be defined as::
- Unlike `create_user()`, `create_superuser()` *must* require the caller
- to provider a password.
+ def create_superuser(self, email, date_of_birth, password):
+ # create superuser here
+
+ Unlike `create_user()`, `create_superuser()` *must* require the caller
+ to provider a password.
:class:`~django.contrib.auth.models.BaseUserManager` provides the following
utility methods:
.. class:: models.BaseUserManager
+
.. method:: models.BaseUserManager.normalize_email(email)
A classmethod that normalizes email addresses by lowercasing
@@ -2165,12 +2175,12 @@ authentication app::
user.save(using=self._db)
return user
- def create_superuser(self, username, date_of_birth, password):
+ def create_superuser(self, email, date_of_birth, password):
"""
Creates and saves a superuser with the given email, date of
birth and password.
"""
- user = self.create_user(username,
+ user = self.create_user(email,
password=password,
date_of_birth=date_of_birth
)
@@ -2223,7 +2233,7 @@ authentication app::
return self.is_admin
Then, to register this custom User model with Django's admin, the following
-code would be required in ``admin.py``::
+code would be required in the app's ``admin.py`` file::
from django import forms
from django.contrib import admin
@@ -2249,7 +2259,7 @@ code would be required in ``admin.py``::
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
- raise forms.ValidationError('Passwords don't match')
+ raise forms.ValidationError("Passwords don't match")
return password2
def save(self, commit=True):
Please sign in to comment.
Something went wrong with that request. Please try again.