Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge da1daf9 into c2150d4

  • Loading branch information...
commit 6c7dba29a6c064f14eade5496c04fb690b399a8a 2 parents c2150d4 + da1daf9
Preston Holmes authored October 09, 2012
77  django/contrib/auth/management/commands/createsuperuser.py
@@ -16,22 +16,38 @@
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
+        # an __init__ method is here largely to support swapping out custom user
  22
+        # models in tests
  23
+        super(Command, self).__init__(*args, **kwargs)
  24
+        self.UserModel = get_user_model()
  25
+        self.required_fields = self.UserModel.REQUIRED_FIELDS
  26
+        self.username_field_name = getattr(self.UserModel, 'USERNAME_FIELD', 'username')
  27
+        self.username_field = self.UserModel._meta.get_field(self.username_field_name)
  28
+        if 'username' in self.required_fields and not self.username_field_name == 'username':
  29
+            CommandError('Custom User objects requiring a "username" field must'
  30
+                    'designate it as the USERNAME_FIELD. This is required when'
  31
+                    'creating superusers')
  32
+
  33
+        self.option_list = BaseCommand.option_list + (
  34
+            make_option('--%s' % self.username_field_name, dest='username', default=None,
  35
+                help='Specifies the %s for the superuser.' % self.username_field_name),
  36
+            make_option('--noinput', action='store_false', dest='interactive', default=True,
  37
+                help=('Tells Django to NOT prompt the user for input of any kind. '
  38
+                    'You must use --%s with --noinput, along with an option for '
  39
+                    'any other required field. Superusers created with --noinput will '
  40
+                    ' not be able to log in until they\'re given a valid password.' %
  41
+                    self.username_field_name)),
  42
+            make_option('--database', action='store', dest='database',
  43
+                default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
  44
+        ) + tuple(
  45
+            make_option('--%s' % field, dest=field, default=None,
  46
+                help='Specifies the %s for the superuser.' % field)
  47
+            for field in self.required_fields
  48
+        )
  49
+
  50
+    option_list = BaseCommand.option_list
35 51
     help = 'Used to create a superuser.'
36 52
 
37 53
     def handle(self, *args, **options):
@@ -40,10 +56,7 @@ def handle(self, *args, **options):
40 56
         verbosity = int(options.get('verbosity', 1))
41 57
         database = options.get('database')
42 58
 
43  
-        UserModel = get_user_model()
44  
-
45  
-        username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
46  
-        other_fields = UserModel.REQUIRED_FIELDS
  59
+        other_fields = self.required_fields
47 60
 
48 61
         # If not provided, create the user with an unusable password
49 62
         password = None
@@ -53,12 +66,13 @@ def handle(self, *args, **options):
53 66
         if not interactive:
54 67
             try:
55 68
                 if not username:
56  
-                    raise CommandError("You must use --username with --noinput.")
57  
-                username = username_field.clean(username, None)
  69
+                    raise CommandError("You must use --%s with --noinput." %
  70
+                            self.username_field_name)
  71
+                username = self.username_field.clean(username, None)
58 72
 
59 73
                 for field_name in other_fields:
60 74
                     if options.get(field_name):
61  
-                        field = UserModel._meta.get_field(field_name)
  75
+                        field = self.UserModel._meta.get_field(field_name)
62 76
                         other_data[field_name] = field.clean(options[field_name], None)
63 77
                     else:
64 78
                         raise CommandError("You must use --%s with --noinput." % field_name)
@@ -74,9 +88,8 @@ def handle(self, *args, **options):
74 88
 
75 89
                 # Get a username
76 90
                 while username is None:
77  
-                    username_field = UserModel._meta.get_field(getattr(UserModel, 'USERNAME_FIELD', 'username'))
78 91
                     if not username:
79  
-                        input_msg = capfirst(username_field.verbose_name)
  92
+                        input_msg = capfirst(self.username_field.verbose_name)
80 93
                         if default_username:
81 94
                             input_msg += " (leave blank to use '%s')" % default_username
82 95
                         raw_value = input(input_msg + ': ')
@@ -84,23 +97,23 @@ def handle(self, *args, **options):
84 97
                     if default_username and raw_value == '':
85 98
                         raw_value = default_username
86 99
                     try:
87  
-                        username = username_field.clean(raw_value, None)
  100
+                        username = self.username_field.clean(raw_value, None)
88 101
                     except exceptions.ValidationError as e:
89 102
                         self.stderr.write("Error: %s" % '; '.join(e.messages))
90 103
                         username = None
91 104
                         continue
92 105
                     try:
93  
-                        UserModel.objects.using(database).get(**{
94  
-                                getattr(UserModel, 'USERNAME_FIELD', 'username'): username
95  
-                            })
96  
-                    except UserModel.DoesNotExist:
  106
+                        self.UserModel.objects.using(database).get_by_natural_key(
  107
+                                username)
  108
+                    except self.UserModel.DoesNotExist:
97 109
                         pass
98 110
                     else:
99  
-                        self.stderr.write("Error: That username is already taken.")
  111
+                        self.stderr.write("Error: That %s is already taken." %
  112
+                                self.username_field.verbose_name)
100 113
                         username = None
101 114
 
102 115
                 for field_name in other_fields:
103  
-                    field = UserModel._meta.get_field(field_name)
  116
+                    field = self.UserModel._meta.get_field(field_name)
104 117
                     other_data[field_name] = options.get(field_name)
105 118
                     while other_data[field_name] is None:
106 119
                         raw_value = input(capfirst(field.verbose_name + ': '))
@@ -128,6 +141,6 @@ def handle(self, *args, **options):
128 141
                 self.stderr.write("\nOperation cancelled.")
129 142
                 sys.exit(1)
130 143
 
131  
-        UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
  144
+        self.UserModel.objects.db_manager(database).create_superuser(username=username, password=password, **other_data)
132 145
         if verbosity >= 1:
133 146
             self.stdout.write("Superuser created successfully.")
19  django/contrib/auth/tests/custom_user.py
@@ -88,3 +88,22 @@ class ExtensionUser(AbstractUser):
88 88
 
89 89
     class Meta:
90 90
         app_label = 'auth'
  91
+
  92
+
  93
+class InvalidUsernameUser(AbstractUser):
  94
+    """
  95
+    A test custom user which has a required username field that is not used
  96
+    as the USERNAME_FIELD. This is invalid for createsuperuser management
  97
+    command which uses the 'username' kwarg in it's signature
  98
+    """
  99
+
  100
+    ident = models.CharField(max_length=30)
  101
+
  102
+    objects = UserManager()
  103
+
  104
+    USERNAME_FIELD = 'ident'
  105
+    REQUIRED_FIELDS = AbstractUser.REQUIRED_FIELDS + ['username']
  106
+
  107
+    class Meta:
  108
+        app_label = 'auth'
  109
+
17  django/contrib/auth/tests/management.py
@@ -151,6 +151,23 @@ def test_swappable_user(self):
151 151
         # created password should be unusable
152 152
         self.assertFalse(u.has_usable_password())
153 153
 
  154
+    @override_settings(AUTH_USER_MODEL='auth.InvalidUsernameUser')
  155
+    def test_invalid_required_fields(self):
  156
+        """
  157
+        A custom user model having 'username' in REQUIRED_FIELDS without using
  158
+        it as USERNAME_FIELD is invalid because of the existing signature of
  159
+        'createsuperuser' using the username kwarg
  160
+        """
  161
+
  162
+        new_io = StringIO()
  163
+        with self.assertRaises(CommandError):
  164
+            call_command("createsuperuser",
  165
+                    interactive=False,
  166
+                    username='foo',
  167
+                    stdout=new_io,
  168
+                    skip_validation=True
  169
+            )
  170
+
154 171
     @override_settings(AUTH_USER_MODEL='auth.CustomUser')
155 172
     def test_swappable_user_missing_required_field(self):
156 173
         "A Custom superuser won't be created when a required field isn't provided"
9  docs/topics/auth.txt
@@ -1897,6 +1897,15 @@ password resets. You must then provide some key implementation details:
1897 1897
             ...
1898 1898
             REQUIRED_FIELDS = ['date_of_birth', 'height']
1899 1899
 
  1900
+.. note::
  1901
+
  1902
+    ``REQUIRED_FIELDS`` contains required fields that are in addition to the
  1903
+    field specified by ``USERNAME_FIELD``, you do not need to include it here
  1904
+    again. Also note that if you are going to have a field named ``username``
  1905
+    which you want to be required, it must be used as the ``USERNAME_FIELD``
  1906
+    attribute - this is a requirement of the
  1907
+    :meth:`~django.contrib.auth.models.UserManager.create_superuser` method.
  1908
+
1900 1909
 .. method:: User.get_full_name():
1901 1910
 
1902 1911
     A longer formal identifier for the user. A common interpretation

0 notes on commit 6c7dba2

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