Development of an internal social media platform with personalised dashboards for students
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 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. class Command(BaseCommand):
  16. help = 'Used to create a superuser.'
  17. requires_migrations_checks = True
  18. stealth_options = ('stdin',)
  19. def __init__(self, *args, **kwargs):
  20. super().__init__(*args, **kwargs)
  21. self.UserModel = get_user_model()
  22. self.username_field = self.UserModel._meta.get_field(self.UserModel.USERNAME_FIELD)
  23. def add_arguments(self, parser):
  24. parser.add_argument(
  25. '--%s' % self.UserModel.USERNAME_FIELD,
  26. dest=self.UserModel.USERNAME_FIELD, default=None,
  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', action='store', dest='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, dest=field, default=None,
  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. # If not provided, create the user with an unusable password
  56. password = None
  57. user_data = {}
  58. # Same as user_data but with foreign keys as fake model instances
  59. # instead of raw IDs.
  60. fake_user_data = {}
  61. verbose_field_name = self.username_field.verbose_name
  62. # Do quick and dirty validation if --noinput
  63. if not options['interactive']:
  64. try:
  65. if not username:
  66. raise CommandError("You must use --%s with --noinput." % self.UserModel.USERNAME_FIELD)
  67. username = self.username_field.clean(username, None)
  68. for field_name in self.UserModel.REQUIRED_FIELDS:
  69. if options[field_name]:
  70. field = self.UserModel._meta.get_field(field_name)
  71. user_data[field_name] = field.clean(options[field_name], None)
  72. else:
  73. raise CommandError("You must use --%s with --noinput." % field_name)
  74. except exceptions.ValidationError as e:
  75. raise CommandError('; '.join(e.messages))
  76. else:
  77. # Prompt for username/password, and any other required fields.
  78. # Enclose this whole thing in a try/except to catch
  79. # KeyboardInterrupt and exit gracefully.
  80. default_username = get_default_username()
  81. try:
  82. if hasattr(self.stdin, 'isatty') and not self.stdin.isatty():
  83. raise NotRunningInTTYException("Not running in a TTY")
  84. # Get a username
  85. while username is None:
  86. input_msg = capfirst(verbose_field_name)
  87. if default_username:
  88. input_msg += " (leave blank to use '%s')" % default_username
  89. username_rel = self.username_field.remote_field
  90. input_msg = '%s%s: ' % (
  91. input_msg,
  92. ' (%s.%s)' % (
  93. username_rel.model._meta.object_name,
  94. username_rel.field_name
  95. ) if username_rel else ''
  96. )
  97. username = self.get_input_data(self.username_field, input_msg, default_username)
  98. if not username:
  99. continue
  100. if self.username_field.unique:
  101. try:
  102. self.UserModel._default_manager.db_manager(database).get_by_natural_key(username)
  103. except self.UserModel.DoesNotExist:
  104. pass
  105. else:
  106. self.stderr.write("Error: That %s is already taken." % verbose_field_name)
  107. username = None
  108. if not username:
  109. raise CommandError('%s cannot be blank.' % capfirst(verbose_field_name))
  110. for field_name in self.UserModel.REQUIRED_FIELDS:
  111. field = self.UserModel._meta.get_field(field_name)
  112. user_data[field_name] = options[field_name]
  113. while user_data[field_name] is None:
  114. message = '%s%s: ' % (
  115. capfirst(field.verbose_name),
  116. ' (%s.%s)' % (
  117. field.remote_field.model._meta.object_name,
  118. field.remote_field.field_name,
  119. ) if field.remote_field else '',
  120. )
  121. input_value = self.get_input_data(field, message)
  122. user_data[field_name] = input_value
  123. fake_user_data[field_name] = input_value
  124. # Wrap any foreign keys in fake model instances
  125. if field.remote_field:
  126. fake_user_data[field_name] = field.remote_field.model(input_value)
  127. # Get a password
  128. while password is None:
  129. password = getpass.getpass()
  130. password2 = getpass.getpass('Password (again): ')
  131. if password != password2:
  132. self.stderr.write("Error: Your passwords didn't match.")
  133. password = None
  134. # Don't validate passwords that don't match.
  135. continue
  136. if password.strip() == '':
  137. self.stderr.write("Error: Blank passwords aren't allowed.")
  138. password = None
  139. # Don't validate blank passwords.
  140. continue
  141. try:
  142. validate_password(password2, self.UserModel(**fake_user_data))
  143. except exceptions.ValidationError as err:
  144. self.stderr.write('\n'.join(err.messages))
  145. response = input('Bypass password validation and create user anyway? [y/N]: ')
  146. if response.lower() != 'y':
  147. password = None
  148. except KeyboardInterrupt:
  149. self.stderr.write("\nOperation cancelled.")
  150. sys.exit(1)
  151. except NotRunningInTTYException:
  152. self.stdout.write(
  153. "Superuser creation skipped due to not running in a TTY. "
  154. "You can run `manage.py createsuperuser` in your project "
  155. "to create one manually."
  156. )
  157. if username:
  158. user_data[self.UserModel.USERNAME_FIELD] = username
  159. user_data['password'] = password
  160. self.UserModel._default_manager.db_manager(database).create_superuser(**user_data)
  161. if options['verbosity'] >= 1:
  162. self.stdout.write("Superuser created successfully.")
  163. def get_input_data(self, field, message, default=None):
  164. """
  165. Override this method if you want to customize data inputs or
  166. validation exceptions.
  167. """
  168. raw_value = input(message)
  169. if default and raw_value == '':
  170. raw_value = default
  171. try:
  172. val = field.clean(raw_value, None)
  173. except exceptions.ValidationError as e:
  174. self.stderr.write("Error: %s" % '; '.join(e.messages))
  175. val = None
  176. return val