Skip to content

Commit

Permalink
Fixed #35408 -- Optimized post-migrate permission creation.
Browse files Browse the repository at this point in the history
co-authored-by: Mariusz Felisiak <felisiak.mariusz@gmail.com>
  • Loading branch information
adamchainz and felixxm committed Apr 26, 2024
1 parent ec85524 commit 2fda5fa
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 15 deletions.
30 changes: 17 additions & 13 deletions django/contrib/auth/management/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ def create_permissions(
if not app_config.models_module:
return

try:
Permission = apps.get_model("auth", "Permission")
except LookupError:
return
if not router.allow_migrate_model(using, Permission):
return

# Ensure that contenttypes are created for this app. Needed if
# 'django.contrib.auth' is in INSTALLED_APPS before
# 'django.contrib.contenttypes'.
Expand All @@ -62,36 +69,33 @@ def create_permissions(
try:
app_config = apps.get_app_config(app_label)
ContentType = apps.get_model("contenttypes", "ContentType")
Permission = apps.get_model("auth", "Permission")
except LookupError:
return

if not router.allow_migrate_model(using, Permission):
return

models = list(app_config.get_models())
# Grab all the content types from the current database.
ctypes = ContentType.objects.db_manager(using).get_for_models(
*models, for_concrete_models=False
)
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = []
# The codenames and ctypes that should exist.
ctypes = set()
for klass in app_config.get_models():
# Force looking up the content types in the current database
# before creating foreign keys to them.
ctype = ContentType.objects.db_manager(using).get_for_model(
klass, for_concrete_model=False
for model in models:
ctype = ctypes[model]
searched_perms.extend(
[(ctype, perm) for perm in _get_all_permissions(model._meta)]
)

ctypes.add(ctype)
for perm in _get_all_permissions(klass._meta):
searched_perms.append((ctype, perm))

# Find all the Permissions that have a content_type for a model we're
# looking for. We don't need to check for codenames since we already have
# a list of the ones we're going to create.
all_perms = set(
Permission.objects.using(using)
.filter(
content_type__in=ctypes,
content_type__in=set(ctypes.values()),
)
.values_list("content_type", "codename")
)
Expand Down
2 changes: 1 addition & 1 deletion tests/auth_tests/test_management.py
Original file line number Diff line number Diff line change
Expand Up @@ -1528,7 +1528,7 @@ class CreatePermissionsMultipleDatabasesTests(TestCase):

def test_set_permissions_fk_to_using_parameter(self):
Permission.objects.using("other").delete()
with self.assertNumQueries(6, using="other") as captured_queries:
with self.assertNumQueries(4, using="other") as captured_queries:
create_permissions(apps.get_app_config("auth"), verbosity=0, using="other")
self.assertIn("INSERT INTO", captured_queries[-1]["sql"].upper())
self.assertGreater(Permission.objects.using("other").count(), 0)
2 changes: 1 addition & 1 deletion tests/contenttypes_tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def test_lookup_cache(self):

def test_get_for_models_creation(self):
ContentType.objects.all().delete()
with self.assertNumQueries(4):
with self.assertNumQueries(2):
cts = ContentType.objects.get_for_models(
ContentType, FooWithUrl, ProxyModel, ConcreteModel
)
Expand Down

0 comments on commit 2fda5fa

Please sign in to comment.