Fixed #21832 -- Updated prompt, tests, and docs to show that USERNAME_FIELD supports FK after 9bc2d76
.
Also added get_input_data() hook in createsuperuser. Thanks Chris Jerdonek and Tim Graham for review.
This commit is contained in:
parent
136a3ffe21
commit
75ff7b8fb8
|
@ -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):
|
||||||
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):
|
||||||
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):
|
||||||
|
|
||||||
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):
|
||||||
|
|
||||||
|
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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.
|
||||||
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
|
||||||
* 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:
|
||||||
...
|
...
|
||||||
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
|
||||||
|
|
Loading…
Reference in New Issue