diff --git a/django/contrib/auth/management/__init__.py b/django/contrib/auth/management/__init__.py index cdf7203a9d..7c9618c63b 100644 --- a/django/contrib/auth/management/__init__.py +++ b/django/contrib/auth/management/__init__.py @@ -6,6 +6,7 @@ import unicodedata from django.apps import apps as global_apps from django.contrib.auth import get_permission_codename +from django.contrib.contenttypes.management import create_contenttypes from django.core import exceptions from django.db import DEFAULT_DB_ALIAS, router @@ -37,6 +38,11 @@ def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_ if not app_config.models_module: return + # Ensure that contenttypes are created for this app. Needed if + # 'django.contrib.auth' is in INSTALLED_APPS before + # 'django.contrib.contenttypes'. + create_contenttypes(app_config, verbosity=verbosity, interactive=interactive, using=using, apps=apps, **kwargs) + app_label = app_config.label try: app_config = apps.get_app_config(app_label) diff --git a/tests/auth_tests/test_management.py b/tests/auth_tests/test_management.py index 4f29740982..73cf66699b 100644 --- a/tests/auth_tests/test_management.py +++ b/tests/auth_tests/test_management.py @@ -838,3 +838,15 @@ class CreatePermissionsTests(TestCase): state = migrations.state.ProjectState(real_apps=['contenttypes']) with self.assertNumQueries(0): create_permissions(self.app_config, verbosity=0, apps=state.apps) + + def test_create_permissions_checks_contenttypes_created(self): + """ + `post_migrate` handler ordering isn't guaranteed. Simulate a case + where create_permissions() is called before create_contenttypes(). + """ + # Warm the manager cache. + ContentType.objects.get_for_model(Group) + # Apply a deletion as if e.g. a database 'flush' had been executed. + ContentType.objects.filter(app_label='auth', model='group').delete() + # This fails with a foreign key constraint without the fix. + create_permissions(apps.get_app_config('auth'), interactive=False, verbosity=0)