diff options
| author | Hasan Ramezani <hasan.r67@gmail.com> | 2020-03-10 22:22:23 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-07-06 11:47:22 +0200 |
| commit | 7af8f4127397279d19ef7c7899e93018274e2f9b (patch) | |
| tree | a3e86b0e02d376352ac204e76c6c1e36fd230941 | |
| parent | d1409f51ff40f6d0ca875c4fafc4189dbe258b37 (diff) | |
Refs #26445 -- Allowed using UserManager.create_user()/create_superuser() in migrations.
Used app config to lookup user model in _create_user().
Thanks Markus Holtermann for the review and initial patch.
Thanks Simon Charette for the implementation idea.
| -rw-r--r-- | django/contrib/auth/models.py | 10 | ||||
| -rw-r--r-- | tests/auth_tests/test_models.py | 37 |
2 files changed, 43 insertions, 4 deletions
diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 84962b9d0e..5f092f0ae8 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -1,5 +1,7 @@ +from django.apps import apps from django.contrib import auth from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager +from django.contrib.auth.hashers import make_password from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.core.mail import send_mail @@ -134,9 +136,13 @@ class UserManager(BaseUserManager): if not username: raise ValueError('The given username must be set') email = self.normalize_email(email) - username = self.model.normalize_username(username) + # Lookup the real model class from the global app registry so this + # manager method can be used in migrations. This is fine because + # managers are by definition working on the real model. + GlobalUserModel = apps.get_model(self.model._meta.app_label, self.model._meta.object_name) + username = GlobalUserModel.normalize_username(username) user = self.model(username=username, email=email, **extra_fields) - user.set_password(password) + user.password = make_password(password) user.save(using=self._db) return user diff --git a/tests/auth_tests/test_models.py b/tests/auth_tests/test_models.py index c60b66c993..2379f68b83 100644 --- a/tests/auth_tests/test_models.py +++ b/tests/auth_tests/test_models.py @@ -10,8 +10,12 @@ from django.contrib.auth.models import ( ) from django.contrib.contenttypes.models import ContentType from django.core import mail +from django.db import connection, migrations +from django.db.migrations.state import ModelState, ProjectState from django.db.models.signals import post_save -from django.test import SimpleTestCase, TestCase, override_settings +from django.test import ( + SimpleTestCase, TestCase, TransactionTestCase, override_settings, +) from .models import IntegerUsernameUser from .models.with_custom_email_field import CustomEmailField @@ -101,7 +105,12 @@ class LoadDataWithNaturalKeysAndMultipleDatabasesTestCase(TestCase): self.assertEqual(perm_other.content_type_id, other_objects[0].id) -class UserManagerTestCase(TestCase): +class UserManagerTestCase(TransactionTestCase): + available_apps = [ + 'auth_tests', + 'django.contrib.auth', + 'django.contrib.contenttypes', + ] def test_create_user(self): email_lowercase = 'normal@normal.com' @@ -156,6 +165,30 @@ class UserManagerTestCase(TestCase): for char in password: self.assertIn(char, allowed_chars) + def test_runpython_manager_methods(self): + def forwards(apps, schema_editor): + UserModel = apps.get_model('auth', 'User') + user = UserModel.objects.create_user('user1', password='secure') + self.assertIsInstance(user, UserModel) + + operation = migrations.RunPython(forwards, migrations.RunPython.noop) + project_state = ProjectState() + project_state.add_model(ModelState.from_model(User)) + project_state.add_model(ModelState.from_model(Group)) + project_state.add_model(ModelState.from_model(Permission)) + project_state.add_model(ModelState.from_model(ContentType)) + new_state = project_state.clone() + with connection.schema_editor() as editor: + operation.state_forwards('test_manager_methods', new_state) + operation.database_forwards( + 'test_manager_methods', + editor, + project_state, + new_state, + ) + user = User.objects.get(username='user1') + self.assertTrue(user.check_password('secure')) + class AbstractBaseUserTests(SimpleTestCase): |
