Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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 authored
90  django/contrib/auth/management/commands/createsuperuser.py
@@ -16,50 +16,56 @@
16 16
 
17 17
 
18 18
 class Command(BaseCommand):
19  
-    option_list = BaseCommand.option_list + (
20  
-        make_option('--username', dest='username', default=None,
21  
-            help='Specifies the username for the superuser.'),
22  
-        make_option('--noinput', action='store_false', dest='interactive', default=True,
23  
-            help=('Tells Django to NOT prompt the user for input of any kind. '
24  
-                  'You must use --username with --noinput, along with an option for '
25  
-                  'any other required field. Superusers created with --noinput will '
26  
-                  ' not be able to log in until they\'re given a valid password.')),
27  
-        make_option('--database', action='store', dest='database',
28  
-            default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
29  
-    ) + tuple(
30  
-        make_option('--%s' % field, dest=field, default=None,
31  
-            help='Specifies the %s for the superuser.' % field)
32  
-        for field in get_user_model().REQUIRED_FIELDS
33  
-    )
34 19
 
  20
+    def __init__(self, *args, **kwargs):
  21
+        # Options are defined in an __init__ method to support swapping out
  22
+        # custom user models in tests.
  23
+        super(Command, self).__init__(*args, **kwargs)
  24
+        self.UserModel = get_user_model()
  25
+        self.username_field = self.UserModel._meta.get_field(self.UserModel.USERNAME_FIELD)
  26
+
  27
+        self.option_list = BaseCommand.option_list + (
  28
+            make_option('--%s' % self.UserModel.USERNAME_FIELD, dest=self.UserModel.USERNAME_FIELD, default=None,
  29
+                help='Specifies the login for the superuser.'),
  30
+            make_option('--noinput', action='store_false', dest='interactive', default=True,
  31
+                help=('Tells Django to NOT prompt the user for input of any kind. '
  32
+                    'You must use --%s with --noinput, along with an option for '
  33
+                    'any other required field. Superusers created with --noinput will '
  34
+                    ' not be able to log in until they\'re given a valid password.' %
  35
+                    self.UserModel.USERNAME_FIELD)),
  36
+            make_option('--database', action='store', dest='database',
  37
+                default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
  38
+        ) + tuple(
  39
+            make_option('--%s' % field, dest=field, default=None,
  40
+                help='Specifies the %s for the superuser.' % field)
  41
+            for field in self.UserModel.REQUIRED_FIELDS
  42
+        )
  43
+
  44
+    option_list = BaseCommand.option_list
35 45
     help = 'Used to create a superuser.'
36 46
 
37 47
     def handle(self, *args, **options):
38  
-        username = options.get('username', None)
  48
+        username = options.get(self.UserModel.USERNAME_FIELD, None)
39 49
         interactive = options.get('interactive')
40 50
         verbosity = int(options.get('verbosity', 1))
41 51
         database = options.get('database')
42 52
 
43  
-        UserModel = get_user_model()
44  
-
45  
-        username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
46  
-        other_fields = UserModel.REQUIRED_FIELDS
47  
-
48 53
         # If not provided, create the user with an unusable password
49 54
         password = None
50  
-        other_data = {}
  55
+        user_data = {}
51 56
 
52 57
         # Do quick and dirty validation if --noinput
53 58
         if not interactive:
54 59
             try:
55 60
                 if not username:
56  
-                    raise CommandError("You must use --username with --noinput.")
57  
-                username = username_field.clean(username, None)
  61
+                    raise CommandError("You must use --%s with --noinput." %
  62
+                            self.UserModel.USERNAME_FIELD)
  63
+                username = self.username_field.clean(username, None)
58 64
 
59  
-                for field_name in other_fields:
  65
+                for field_name in self.UserModel.REQUIRED_FIELDS:
60 66
                     if options.get(field_name):
61  
-                        field = UserModel._meta.get_field(field_name)
62  
-                        other_data[field_name] = field.clean(options[field_name], None)
  67
+                        field = self.UserModel._meta.get_field(field_name)
  68
+                        user_data[field_name] = field.clean(options[field_name], None)
63 69
                     else:
64 70
                         raise CommandError("You must use --%s with --noinput." % field_name)
65 71
             except exceptions.ValidationError as e:
@@ -74,9 +80,8 @@ def handle(self, *args, **options):
74 80
 
75 81
                 # Get a username
76 82
                 while username is None:
77  
-                    username_field = UserModel._meta.get_field(UserModel.USERNAME_FIELD)
78 83
                     if not username:
79  
-                        input_msg = capfirst(username_field.verbose_name)
  84
+                        input_msg = capfirst(self.username_field.verbose_name)
80 85
                         if default_username:
81 86
                             input_msg += " (leave blank to use '%s')" % default_username
82 87
                         raw_value = input(input_msg + ': ')
@@ -84,31 +89,30 @@ def handle(self, *args, **options):
84 89
                     if default_username and raw_value == '':
85 90
                         raw_value = default_username
86 91
                     try:
87  
-                        username = username_field.clean(raw_value, None)
  92
+                        username = self.username_field.clean(raw_value, None)
88 93
                     except exceptions.ValidationError as e:
89 94
                         self.stderr.write("Error: %s" % '; '.join(e.messages))
90 95
                         username = None
91 96
                         continue
92 97
                     try:
93  
-                        UserModel.objects.using(database).get(**{
94  
-                                UserModel.USERNAME_FIELD: username
95  
-                            })
96  
-                    except UserModel.DoesNotExist:
  98
+                        self.UserModel.objects.db_manager(database).get_by_natural_key(username)
  99
+                    except self.UserModel.DoesNotExist:
97 100
                         pass
98 101
                     else:
99  
-                        self.stderr.write("Error: That username is already taken.")
  102
+                        self.stderr.write("Error: That %s is already taken." %
  103
+                                self.username_field.verbose_name)
100 104
                         username = None
101 105
 
102  
-                for field_name in other_fields:
103  
-                    field = UserModel._meta.get_field(field_name)
104  
-                    other_data[field_name] = options.get(field_name)
105  
-                    while other_data[field_name] is None:
  106
+                for field_name in self.UserModel.REQUIRED_FIELDS:
  107
+                    field = self.UserModel._meta.get_field(field_name)
  108
+                    user_data[field_name] = options.get(field_name)
  109
+                    while user_data[field_name] is None:
106 110
                         raw_value = input(capfirst(field.verbose_name + ': '))
107 111
                         try:
108  
-                            other_data[field_name] = field.clean(raw_value, None)
  112
+                            user_data[field_name] = field.clean(raw_value, None)
109 113
                         except exceptions.ValidationError as e:
110 114
                             self.stderr.write("Error: %s" % '; '.join(e.messages))
111  
-                            other_data[field_name] = None
  115
+                            user_data[field_name] = None
112 116
 
113 117
                 # Get a password
114 118
                 while password is None:
@@ -128,6 +132,8 @@ def handle(self, *args, **options):
128 132
                 self.stderr.write("\nOperation cancelled.")
129 133
                 sys.exit(1)
130 134
 
131  
-        UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
  135
+        user_data[self.UserModel.USERNAME_FIELD] = username
  136
+        user_data['password'] = password
  137
+        self.UserModel.objects.db_manager(database).create_superuser(**user_data)
132 138
         if verbosity >= 1:
133 139
             self.stdout.write("Superuser created successfully.")
4  django/contrib/auth/tests/custom_user.py
@@ -23,8 +23,8 @@ def create_user(self, email, date_of_birth, password=None):
23 23
         user.save(using=self._db)
24 24
         return user
25 25
 
26  
-    def create_superuser(self, username, password, date_of_birth):
27  
-        u = self.create_user(username, password=password, date_of_birth=date_of_birth)
  26
+    def create_superuser(self, email, password, date_of_birth):
  27
+        u = self.create_user(email, password=password, date_of_birth=date_of_birth)
28 28
         u.is_admin = True
29 29
         u.save(using=self._db)
30 30
         return u
2  django/contrib/auth/tests/management.py
@@ -138,7 +138,7 @@ def test_swappable_user(self):
138 138
         new_io = StringIO()
139 139
         call_command("createsuperuser",
140 140
             interactive=False,
141  
-            username="joe@somewhere.org",
  141
+            email="joe@somewhere.org",
142 142
             date_of_birth="1976-04-01",
143 143
             stdout=new_io,
144 144
             skip_validation=True
110  docs/topics/auth.txt
@@ -1878,47 +1878,54 @@ The easiest way to construct a compliant custom User model is to inherit from
1878 1878
 implementation of a `User` model, including hashed passwords and tokenized
1879 1879
 password resets. You must then provide some key implementation details:
1880 1880
 
1881  
-.. attribute:: User.USERNAME_FIELD
  1881
+.. class:: models.CustomUser
1882 1882
 
1883  
-    A string describing the name of the field on the User model that is
1884  
-    used as the unique identifier. This will usually be a username of
1885  
-    some kind, but it can also be an email address, or any other unique
1886  
-    identifier. In the following example, the field `identifier` is used
1887  
-    as the identifying field::
  1883
+    .. attribute:: User.USERNAME_FIELD
1888 1884
 
1889  
-        class MyUser(AbstractBaseUser):
1890  
-            identfier = models.CharField(max_length=40, unique=True, db_index=True)
1891  
-            ...
1892  
-            USERNAME_FIELD = 'identifier'
  1885
+        A string describing the name of the field on the User model that is
  1886
+        used as the unique identifier. This will usually be a username of
  1887
+        some kind, but it can also be an email address, or any other unique
  1888
+        identifier. In the following example, the field `identifier` is used
  1889
+        as the identifying field::
1893 1890
 
1894  
-.. attribute:: User.REQUIRED_FIELDS
  1891
+            class MyUser(AbstractBaseUser):
  1892
+                identfier = models.CharField(max_length=40, unique=True, db_index=True)
  1893
+                ...
  1894
+                USERNAME_FIELD = 'identifier'
1895 1895
 
1896  
-    A list of the field names that *must* be provided when creating
1897  
-    a user. For example, here is the partial definition for a User model
1898  
-    that defines two required fields - a date of birth and height::
  1896
+    .. attribute:: User.REQUIRED_FIELDS
1899 1897
 
1900  
-        class MyUser(AbstractBaseUser):
1901  
-            ...
1902  
-            date_of_birth = models.DateField()
1903  
-            height = models.FloatField()
1904  
-            ...
1905  
-            REQUIRED_FIELDS = ['date_of_birth', 'height']
  1898
+        A list of the field names that *must* be provided when creating
  1899
+        a user. For example, here is the partial definition for a User model
  1900
+        that defines two required fields - a date of birth and height::
  1901
+
  1902
+            class MyUser(AbstractBaseUser):
  1903
+                ...
  1904
+                date_of_birth = models.DateField()
  1905
+                height = models.FloatField()
  1906
+                ...
  1907
+                REQUIRED_FIELDS = ['date_of_birth', 'height']
1906 1908
 
1907  
-.. method:: User.get_full_name():
  1909
+        .. note::
1908 1910
 
1909  
-    A longer formal identifier for the user. A common interpretation
1910  
-    would be the full name name of the user, but it can be any string that
1911  
-    identifies the user.
  1911
+            ``REQUIRED_FIELDS`` must contain all required fields on your User
  1912
+            model, but should *not* contain the ``USERNAME_FIELD``.
1912 1913
 
1913  
-.. method:: User.get_short_name():
  1914
+    .. method:: User.get_full_name():
1914 1915
 
1915  
-    A short, informal identifier for the user. A common interpretation
1916  
-    would be the first name of the user, but it can be any string that
1917  
-    identifies the user in an informal way. It may also return the same
1918  
-    value as :meth:`django.contrib.auth.User.get_full_name()`.
  1916
+        A longer formal identifier for the user. A common interpretation
  1917
+        would be the full name name of the user, but it can be any string that
  1918
+        identifies the user.
  1919
+
  1920
+    .. method:: User.get_short_name():
  1921
+
  1922
+        A short, informal identifier for the user. A common interpretation
  1923
+        would be the first name of the user, but it can be any string that
  1924
+        identifies the user in an informal way. It may also return the same
  1925
+        value as :meth:`django.contrib.auth.User.get_full_name()`.
1919 1926
 
1920 1927
 The following methods are available on any subclass of
1921  
-:class:`~django.contrib.auth.models.AbstractBaseUser`::
  1928
+:class:`~django.contrib.auth.models.AbstractBaseUser`:
1922 1929
 
1923 1930
 .. class:: models.AbstractBaseUser
1924 1931
 
@@ -1979,33 +1986,36 @@ defines different fields, you will need to define a custom manager that
1979 1986
 extends :class:`~django.contrib.auth.models.BaseUserManager` providing two
1980 1987
 additional methods:
1981 1988
 
1982  
-.. method:: UserManager.create_user(username, password=None, **other_fields)
  1989
+.. class:: models.CustomUserManager
1983 1990
 
1984  
-    The prototype of `create_user()` should accept all required fields
1985  
-    as arguments. For example, if your user model defines `username`,
1986  
-    and `date_of_birth` as required fields, then create_user should be
1987  
-    defined as::
  1991
+    .. method:: models.CustomUserManager.create_user(*username_field*, password=None, **other_fields)
1988 1992
 
1989  
-        def create_user(self, username, date_of_birth, password=None):
1990  
-            # create user here
  1993
+        The prototype of `create_user()` should accept the username field,
  1994
+        plus all required fields as arguments. For example, if your user model
  1995
+        uses `email` as the username field, and has `date_of_birth` as a required
  1996
+        fields, then create_user should be defined as::
1991 1997
 
1992  
-.. method:: UserManager.create_superuser(username, password, **other_fields)
  1998
+            def create_user(self, email, date_of_birth, password=None):
  1999
+                # create user here
1993 2000
 
1994  
-    The prototype of `create_superuser()` should accept all required fields
1995  
-    as arguments. For example, if your user model defines `username`,
1996  
-    and `date_of_birth` as required fields, then create_user should be
1997  
-    defined as::
  2001
+    .. method:: models.CustomUserManager.create_superuser(*username_field*, password, **other_fields)
1998 2002
 
1999  
-        def create_superuser(self, username, date_of_birth, password):
2000  
-            # create superuser here
  2003
+        The prototype of `create_user()` should accept the username field,
  2004
+        plus all required fields as arguments. For example, if your user model
  2005
+        uses `email` as the username field, and has `date_of_birth` as a required
  2006
+        fields, then create_superuser should be defined as::
2001 2007
 
2002  
-    Unlike `create_user()`, `create_superuser()` *must* require the caller
2003  
-    to provider a password.
  2008
+            def create_superuser(self, email, date_of_birth, password):
  2009
+                # create superuser here
  2010
+
  2011
+        Unlike `create_user()`, `create_superuser()` *must* require the caller
  2012
+        to provider a password.
2004 2013
 
2005 2014
 :class:`~django.contrib.auth.models.BaseUserManager` provides the following
2006 2015
 utility methods:
2007 2016
 
2008 2017
 .. class:: models.BaseUserManager
  2018
+
2009 2019
     .. method:: models.BaseUserManager.normalize_email(email)
2010 2020
 
2011 2021
         A classmethod that normalizes email addresses by lowercasing
@@ -2165,12 +2175,12 @@ authentication app::
2165 2175
             user.save(using=self._db)
2166 2176
             return user
2167 2177
 
2168  
-        def create_superuser(self, username, date_of_birth, password):
  2178
+        def create_superuser(self, email, date_of_birth, password):
2169 2179
             """
2170 2180
             Creates and saves a superuser with the given email, date of
2171 2181
             birth and password.
2172 2182
             """
2173  
-            user = self.create_user(username,
  2183
+            user = self.create_user(email,
2174 2184
                 password=password,
2175 2185
                 date_of_birth=date_of_birth
2176 2186
             )
@@ -2223,7 +2233,7 @@ authentication app::
2223 2233
             return self.is_admin
2224 2234
 
2225 2235
 Then, to register this custom User model with Django's admin, the following
2226  
-code would be required in ``admin.py``::
  2236
+code would be required in the app's ``admin.py`` file::
2227 2237
 
2228 2238
     from django import forms
2229 2239
     from django.contrib import admin
@@ -2249,7 +2259,7 @@ code would be required in ``admin.py``::
2249 2259
             password1 = self.cleaned_data.get("password1")
2250 2260
             password2 = self.cleaned_data.get("password2")
2251 2261
             if password1 and password2 and password1 != password2:
2252  
-                raise forms.ValidationError('Passwords don't match')
  2262
+                raise forms.ValidationError("Passwords don't match")
2253 2263
             return password2
2254 2264
 
2255 2265
         def save(self, commit=True):

0 notes on commit b3b3db3

Please sign in to comment.
Something went wrong with that request. Please try again.