Kaydet (Commit) 75ff7b8f authored tarafından Anubhav Joshi's avatar Anubhav Joshi Kaydeden (comit) Tim Graham

Fixed #21832 -- Updated prompt, tests, and docs to show that USERNAME_FIELD…

Fixed #21832 -- Updated prompt, tests, and docs to show that USERNAME_FIELD supports FK after 9bc2d766.

Also added get_input_data() hook in createsuperuser.

Thanks Chris Jerdonek and Tim Graham for review.
üst 136a3ffe
...@@ -87,20 +87,14 @@ class Command(BaseCommand): ...@@ -87,20 +87,14 @@ class Command(BaseCommand):
# Get a username # Get a username
verbose_field_name = self.username_field.verbose_name verbose_field_name = self.username_field.verbose_name
while username is None: while username is None:
input_msg = capfirst(verbose_field_name)
if default_username:
input_msg += " (leave blank to use '%s')" % default_username
username_rel = self.username_field.rel
input_msg = force_str('%s%s: ' % (input_msg,
' (%s.%s)' % (username_rel.to._meta.object_name, username_rel.field_name) if username_rel else ''))
username = self.get_input_data(self.username_field, input_msg, default_username)
if not username: if not username:
input_msg = capfirst(verbose_field_name)
if default_username:
input_msg = "%s (leave blank to use '%s')" % (
input_msg, default_username)
raw_value = input(force_str('%s: ' % input_msg))
if default_username and raw_value == '':
raw_value = default_username
try:
username = self.username_field.clean(raw_value, None)
except exceptions.ValidationError as e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
username = None
continue continue
try: try:
self.UserModel._default_manager.db_manager(database).get_by_natural_key(username) self.UserModel._default_manager.db_manager(database).get_by_natural_key(username)
...@@ -115,12 +109,9 @@ class Command(BaseCommand): ...@@ -115,12 +109,9 @@ class Command(BaseCommand):
field = self.UserModel._meta.get_field(field_name) field = self.UserModel._meta.get_field(field_name)
user_data[field_name] = options.get(field_name) user_data[field_name] = options.get(field_name)
while user_data[field_name] is None: while user_data[field_name] is None:
raw_value = input(force_str('%s%s: ' % (capfirst(field.verbose_name), ' (%s.%s)' % (field.rel.to._meta.object_name, field.rel.field_name) if field.rel else ''))) message = force_str('%s%s: ' % (capfirst(field.verbose_name),
try: ' (%s.%s)' % (field.rel.to._meta.object_name, field.rel.field_name) if field.rel else ''))
user_data[field_name] = field.clean(raw_value, None) user_data[field_name] = self.get_input_data(field, message)
except exceptions.ValidationError as e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
user_data[field_name] = None
# Get a password # Get a password
while password is None: while password is None:
...@@ -153,3 +144,19 @@ class Command(BaseCommand): ...@@ -153,3 +144,19 @@ class Command(BaseCommand):
self.UserModel._default_manager.db_manager(database).create_superuser(**user_data) self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
if options['verbosity'] >= 1: if options['verbosity'] >= 1:
self.stdout.write("Superuser created successfully.") self.stdout.write("Superuser created successfully.")
def get_input_data(self, field, message, default=None):
"""
Override this method if you want to customize data inputs or
validation exceptions.
"""
raw_value = input(message)
if default and raw_value == '':
raw_value = default
try:
val = field.clean(raw_value, None)
except exceptions.ValidationError as e:
self.stderr.write("Error: %s" % '; '.join(e.messages))
val = None
return val
...@@ -40,7 +40,7 @@ class CustomUserManager(BaseUserManager): ...@@ -40,7 +40,7 @@ class CustomUserManager(BaseUserManager):
class CustomUserWithFKManager(BaseUserManager): class CustomUserWithFKManager(BaseUserManager):
def create_superuser(self, username, email, group, password): def create_superuser(self, username, email, group, password):
user = self.model(username=username, email_id=email, group_id=group) user = self.model(username_id=username, email_id=email, group_id=group)
user.set_password(password) user.set_password(password)
user.save(using=self._db) user.save(using=self._db)
return user return user
...@@ -96,8 +96,8 @@ class CustomUser(AbstractBaseUser): ...@@ -96,8 +96,8 @@ class CustomUser(AbstractBaseUser):
class CustomUserWithFK(AbstractBaseUser): class CustomUserWithFK(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True) username = models.ForeignKey(Email, related_name='primary')
email = models.ForeignKey(Email, to_field='email') email = models.ForeignKey(Email, to_field='email', related_name='secondary')
group = models.ForeignKey(Group) group = models.ForeignKey(Group)
custom_objects = CustomUserWithFKManager() custom_objects = CustomUserWithFKManager()
......
...@@ -350,15 +350,15 @@ class CreatesuperuserManagementCommandTestCase(TestCase): ...@@ -350,15 +350,15 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
self.assertIs(command.stdin, sys.stdin) self.assertIs(command.stdin, sys.stdin)
@override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK') @override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK')
def test_required_field_with_fk(self): def test_fields_with_fk(self):
new_io = six.StringIO() new_io = six.StringIO()
group = Group.objects.create(name='mygroup') group = Group.objects.create(name='mygroup')
email = Email.objects.create(email='mymail@gmail.com') email = Email.objects.create(email='mymail@gmail.com')
call_command( call_command(
'createsuperuser', 'createsuperuser',
interactive=False, interactive=False,
username='user', username=email.pk,
email='mymail@gmail.com', email=email.email,
group=group.pk, group=group.pk,
stdout=new_io, stdout=new_io,
skip_checks=True, skip_checks=True,
...@@ -366,7 +366,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase): ...@@ -366,7 +366,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
command_output = new_io.getvalue().strip() command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.') self.assertEqual(command_output, 'Superuser created successfully.')
u = CustomUserWithFK._default_manager.get(email=email) u = CustomUserWithFK._default_manager.get(email=email)
self.assertEqual(u.username, "user") self.assertEqual(u.username, email)
self.assertEqual(u.group, group) self.assertEqual(u.group, group)
non_existent_email = 'mymail2@gmail.com' non_existent_email = 'mymail2@gmail.com'
...@@ -375,19 +375,24 @@ class CreatesuperuserManagementCommandTestCase(TestCase): ...@@ -375,19 +375,24 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
call_command( call_command(
'createsuperuser', 'createsuperuser',
interactive=False, interactive=False,
username='user', username=email.pk,
email=non_existent_email, email=non_existent_email,
stdout=new_io, stdout=new_io,
skip_checks=True, skip_checks=True,
) )
@override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK') @override_settings(AUTH_USER_MODEL='auth.CustomUserWithFK')
def test_required_fields_with_fk_interactive(self): def test_fields_with_fk_interactive(self):
new_io = six.StringIO() new_io = six.StringIO()
group = Group.objects.create(name='mygroup') group = Group.objects.create(name='mygroup')
email = Email.objects.create(email='mymail@gmail.com') email = Email.objects.create(email='mymail@gmail.com')
@mock_inputs({'password': "nopasswd", 'username': "user", 'email': "mymail@gmail.com", 'group': group.pk}) @mock_inputs({
'password': 'nopasswd',
'username (email.id)': email.pk,
'email (email.email)': email.email,
'group (group.id)': group.pk,
})
def test(self): def test(self):
call_command( call_command(
'createsuperuser', 'createsuperuser',
...@@ -400,7 +405,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase): ...@@ -400,7 +405,7 @@ class CreatesuperuserManagementCommandTestCase(TestCase):
command_output = new_io.getvalue().strip() command_output = new_io.getvalue().strip()
self.assertEqual(command_output, 'Superuser created successfully.') self.assertEqual(command_output, 'Superuser created successfully.')
u = CustomUserWithFK._default_manager.get(email=email) u = CustomUserWithFK._default_manager.get(email=email)
self.assertEqual(u.username, 'user') self.assertEqual(u.username, email)
self.assertEqual(u.group, group) self.assertEqual(u.group, group)
test(self) test(self)
......
...@@ -1478,6 +1478,16 @@ it when running interactively. ...@@ -1478,6 +1478,16 @@ it when running interactively.
Use the ``--database`` option to specify the database into which the superuser Use the ``--database`` option to specify the database into which the superuser
object will be saved. object will be saved.
.. versionadded:: 1.8
You can subclass the management command and override ``get_input_data()`` if you
want to customize data input and validation. Consult the source code for
details on the existing implementation and the method's parameters. For example,
it could be useful if you have a ``ForeignKey`` in
:attr:`~django.contrib.auth.models.CustomUser.REQUIRED_FIELDS` and want to
allow creating an instance instead of entering the primary key of an existing
instance.
``django.contrib.gis`` ``django.contrib.gis``
---------------------- ----------------------
......
...@@ -49,7 +49,8 @@ Minor features ...@@ -49,7 +49,8 @@ Minor features
* The ``max_length`` of :attr:`Permission.name * The ``max_length`` of :attr:`Permission.name
<django.contrib.auth.models.Permission.name>` has been increased from 50 to <django.contrib.auth.models.Permission.name>` has been increased from 50 to
255 characters. Please run the database migration. 255 characters. Please run the database migration.
* :attr:`~django.contrib.auth.models.CustomUser.REQUIRED_FIELDS` now supports * :attr:`~django.contrib.auth.models.CustomUser.USERNAME_FIELD` and
:attr:`~django.contrib.auth.models.CustomUser.REQUIRED_FIELDS` now supports
:class:`~django.db.models.ForeignKey`\s. :class:`~django.db.models.ForeignKey`\s.
:mod:`django.contrib.formtools` :mod:`django.contrib.formtools`
......
...@@ -508,6 +508,15 @@ password resets. You must then provide some key implementation details: ...@@ -508,6 +508,15 @@ password resets. You must then provide some key implementation details:
... ...
USERNAME_FIELD = 'identifier' USERNAME_FIELD = 'identifier'
.. versionadded:: 1.8
:attr:`USERNAME_FIELD` now supports
:class:`~django.db.models.ForeignKey`\s. Since there is no way to pass
model instances during the :djadmin:`createsuperuser` prompt, expect the
user to enter the value of :attr:`~django.db.models.ForeignKey.to_field`
value (the :attr:`~django.db.models.Field.primary_key` by default) of an
existing instance.
.. attribute:: REQUIRED_FIELDS .. attribute:: REQUIRED_FIELDS
A list of the field names that will be prompted for when creating a A list of the field names that will be prompted for when creating a
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment