You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

createsuperuser.py 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. """
  2. Management utility to create superusers.
  3. """
  4. import getpass
  5. import sys
  6. from django.contrib.auth import get_user_model
  7. from django.contrib.auth.management import get_default_username
  8. from django.contrib.auth.password_validation import validate_password
  9. from django.core import exceptions
  10. from django.core.management.base import BaseCommand, CommandError
  11. from django.db import DEFAULT_DB_ALIAS
  12. from django.utils.text import capfirst
  13. class NotRunningInTTYException(Exception):
  14. pass
  15. PASSWORD_FIELD = 'password'
  16. class Command(BaseCommand):
  17. help = 'Used to create a superuser.'
  18. requires_migrations_checks = True
  19. stealth_options = ('stdin',)
  20. def __init__(self, *args, **kwargs):
  21. super().__init__(*args, **kwargs)
  22. self.UserModel = get_user_model()
  23. self.username_field = self.UserModel._meta.get_field(self.UserModel.USERNAME_FIELD)
  24. def add_arguments(self, parser):
  25. parser.add_argument(
  26. '--%s' % self.UserModel.USERNAME_FIELD,
  27. help='Specifies the login for the superuser.',
  28. )
  29. parser.add_argument(
  30. '--noinput', '--no-input', action='store_false', dest='interactive',
  31. help=(
  32. 'Tells Django to NOT prompt the user for input of any kind. '
  33. 'You must use --%s with --noinput, along with an option for '
  34. 'any other required field. Superusers created with --noinput will '
  35. 'not be able to log in until they\'re given a valid password.' %
  36. self.UserModel.USERNAME_FIELD
  37. ),
  38. )
  39. parser.add_argument(
  40. '--database',
  41. default=DEFAULT_DB_ALIAS,
  42. help='Specifies the database to use. Default is "default".',
  43. )
  44. for field in self.UserModel.REQUIRED_FIELDS:
  45. parser.add_argument(
  46. '--%s' % field,
  47. help='Specifies the %s for the superuser.' % field,
  48. )
  49. def execute(self, *args, **options):
  50. self.stdin = options.get('stdin', sys.stdin) # Used for testing
  51. return super().execute(*args, **options)
  52. def handle(self, *args, **options):
  53. username = options[self.UserModel.USERNAME_FIELD]
  54. database = options['database']
  55. user_data = {}
  56. verbose_field_name = self.username_field.verbose_name
  57. try:
  58. self.UserModel._meta.get_field(PASSWORD_FIELD)
  59. except exceptions.FieldDoesNotExist:
  60. pass
  61. else:
  62. # If not provided, create the user with an unusable password.
  63. user_data[PASSWORD_FIELD] = None
  64. try:
  65. if options['interactive']:
  66. # Same as user_data but with foreign keys as fake model
  67. # instances instead of raw IDs.
  68. fake_user_data = {}
  69. if hasattr(self.stdin, 'isatty') and not self.stdin.isatty():
  70. raise NotRunningInTTYException
  71. default_username = get_default_username()
  72. if username:
  73. error_msg = self._validate_username(username, verbose_field_name, database)
  74. if error_msg:
  75. self.stderr.write(error_msg)
  76. username = None
  77. elif username == '':
  78. raise CommandError('%s cannot be blank.' % capfirst(verbose_field_name))
  79. # Prompt for username.
  80. while username is None:
  81. message = self._get_input_message(self.username_field, default_username)
  82. username = self.get_input_data(self.username_field, message, default_username)
  83. if username:
  84. error_msg = self._validate_username(username, verbose_field_name, database)
  85. if error_msg:
  86. self.stderr.write(error_msg)
  87. username = None
  88. continue
  89. user_data[self.UserModel.USERNAME_FIELD] = username
  90. fake_user_data[self.UserModel.USERNAME_FIELD] = (
  91. self.username_field.remote_field.model(username)
  92. if self.username_field.remote_field else username
  93. )
  94. # Prompt for required fields.
  95. for field_name in self.UserModel.REQUIRED_FIELDS:
  96. field = self.UserModel._meta.get_field(field_name)
  97. user_data[field_name] = options[field_name]
  98. while user_data[field_name] is None:
  99. message = self._get_input_message(field)
  100. input_value = self.get_input_data(field, message)
  101. user_data[field_name] = input_value
  102. fake_user_data[field_name] = input_value
  103. # Wrap any foreign keys in fake model instances
  104. if field.remote_field:
  105. fake_user_data[field_name] = field.remote_field.model(input_value)
  106. # Prompt for a password if the model has one.
  107. while PASSWORD_FIELD in user_data and user_data[PASSWORD_FIELD] is None:
  108. password = getpass.getpass()
  109. password2 = getpass.getpass('Password (again): ')
  110. if password != password2:
  111. self.stderr.write("Error: Your passwords didn't match.")
  112. # Don't validate passwords that don't match.
  113. continue
  114. if password.strip() == '':
  115. self.stderr.write("Error: Blank passwords aren't allowed.")
  116. # Don't validate blank passwords.
  117. continue
  118. try:
  119. validate_password(password2, self.UserModel(**fake_user_data))
  120. except exceptions.ValidationError as err:
  121. self.stderr.write('\n'.join(err.messages))
  122. response = input('Bypass password validation and create user anyway? [y/N]: ')
  123. if response.lower() != 'y':
  124. continue
  125. user_data[PASSWORD_FIELD] = password
  126. else:
  127. # Non-interactive mode.
  128. if username is None:
  129. raise CommandError('You must use --%s with --noinput.' % self.UserModel.USERNAME_FIELD)
  130. else:
  131. error_msg = self._validate_username(username, verbose_field_name, database)
  132. if error_msg:
  133. raise CommandError(error_msg)
  134. user_data[self.UserModel.USERNAME_FIELD] = username
  135. for field_name in self.UserModel.REQUIRED_FIELDS:
  136. if options[field_name]:
  137. field = self.UserModel._meta.get_field(field_name)
  138. user_data[field_name] = field.clean(options[field_name], None)
  139. else:
  140. raise CommandError('You must use --%s with --noinput.' % field_name)
  141. self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
  142. if options['verbosity'] >= 1:
  143. self.stdout.write("Superuser created successfully.")
  144. except KeyboardInterrupt:
  145. self.stderr.write('\nOperation cancelled.')
  146. sys.exit(1)
  147. except exceptions.ValidationError as e:
  148. raise CommandError('; '.join(e.messages))
  149. except NotRunningInTTYException:
  150. self.stdout.write(
  151. 'Superuser creation skipped due to not running in a TTY. '
  152. 'You can run `manage.py createsuperuser` in your project '
  153. 'to create one manually.'
  154. )
  155. def get_input_data(self, field, message, default=None):
  156. """
  157. Override this method if you want to customize data inputs or
  158. validation exceptions.
  159. """
  160. raw_value = input(message)
  161. if default and raw_value == '':
  162. raw_value = default
  163. try:
  164. val = field.clean(raw_value, None)
  165. except exceptions.ValidationError as e:
  166. self.stderr.write("Error: %s" % '; '.join(e.messages))
  167. val = None
  168. return val
  169. def _get_input_message(self, field, default=None):
  170. return '%s%s%s: ' % (
  171. capfirst(field.verbose_name),
  172. " (leave blank to use '%s')" % default if default else '',
  173. ' (%s.%s)' % (
  174. field.remote_field.model._meta.object_name,
  175. field.remote_field.field_name,
  176. ) if field.remote_field else '',
  177. )
  178. def _validate_username(self, username, verbose_field_name, database):
  179. """Validate username. If invalid, return a string error message."""
  180. if self.username_field.unique:
  181. try:
  182. self.UserModel._default_manager.db_manager(database).get_by_natural_key(username)
  183. except self.UserModel.DoesNotExist:
  184. pass
  185. else:
  186. return 'Error: That %s is already taken.' % verbose_field_name
  187. if not username:
  188. return '%s cannot be blank.' % capfirst(verbose_field_name)
  189. try:
  190. self.username_field.clean(username, None)
  191. except exceptions.ValidationError as e:
  192. return '; '.join(e.messages)