java-topology/defects/django/patch/django-0005-alt-constraints-name-set.md

2.4 KiB
Raw Blame History

UNDF: UNDF-2026-000000378

django-0005: alt_constraints_name list → set in create_altered_constraints()

Severity

MEDIUM

Location

django/db/migrations/autodetector.pycreate_altered_constraints()

Description

alt_constraints_name is built as a plain list and appended to inside a double for old_c / for new_c loop. It is then searched with c.name not in alt_constraints_name twice in list comprehensions that iterate over new_constraints and old_constraints.

Worst-case: N models × C old constraints × C new constraints inner loop builds alt_constraints_name with up to C entries; the two filter comprehensions then each scan that list O(C) per element → overall O(N × C³) where C = constraint count per model. In Django projects with many unique/check constraints the auto-detector fires on every makemigrations call.

Defective code (lines 15551581)

alt_constraints_name = []          # ← plain list
...
for old_c in old_constraints:
    for new_c in new_constraints:
        ...
        if ...:
            alt_constraints_name.append(new_c.name)   # ← O(1) append is fine

add_constraints = [
    c
    for c in new_constraints
    if c not in old_constraints and c.name not in alt_constraints_name  # ← O(C) scan
]
rem_constraints = [
    c
    for c in old_constraints
    if c not in new_constraints and c.name not in alt_constraints_name  # ← O(C) scan
]

Fix

-            alt_constraints_name = []
+            alt_constraints_name = set()
 ...
-                        alt_constraints_name.append(new_c.name)
+                        alt_constraints_name.add(new_c.name)

Patch

--- a/django/db/migrations/autodetector.py
+++ b/django/db/migrations/autodetector.py
@@ -1553,7 +1553,7 @@ class MigrationAutodetector:
             alt_constraints = []
-            alt_constraints_name = []
+            alt_constraints_name = set()

             for old_c in old_constraints:
                 for new_c in new_constraints:
@@ -1567,7 +1567,7 @@ class MigrationAutodetector:
                         ):
                             alt_constraints.append(new_c)
-                            alt_constraints_name.append(new_c.name)
+                            alt_constraints_name.add(new_c.name)

Complexity

  • Before: O(N ×× C) = O(N × C³)
  • After: O(N × C²) — the membership tests drop to O(1)