瀏覽代碼

[5.0.x] Fixed #35329 -- Fixed migrations crash when adding partial unique constraints with nulls_distinct.

Bug in 595a2abb58e04caa4d55fb2589bb80fb2a8fdfa1.

Thanks Lucas Lemke Saunitti for the report.
Backport of b98271a6e42107233311d17f5d7bc74fbb47f22c from main
Mariusz Felisiak 1 年之前
父節點
當前提交
345e3cf57f
共有 3 個文件被更改,包括 37 次插入1 次删除
  1. 1 1
      django/db/backends/base/schema.py
  2. 4 0
      docs/releases/5.0.4.txt
  3. 32 0
      tests/schema/tests.py

+ 1 - 1
django/db/backends/base/schema.py

@@ -129,7 +129,7 @@ class BaseDatabaseSchemaEditor:
     )
     sql_create_unique_index = (
         "CREATE UNIQUE INDEX %(name)s ON %(table)s "
-        "(%(columns)s)%(include)s%(condition)s%(nulls_distinct)s"
+        "(%(columns)s)%(include)s%(nulls_distinct)s%(condition)s"
     )
     sql_rename_index = "ALTER INDEX %(old_name)s RENAME TO %(new_name)s"
     sql_delete_index = "DROP INDEX %(name)s"

+ 4 - 0
docs/releases/5.0.4.txt

@@ -17,3 +17,7 @@ Bugfixes
 * Fixed a regression in Django 5.0 where the ``AdminFileWidget`` could be
   rendered with two ``id`` attributes on the "Clear" checkbox
   (:ticket:`35273`).
+
+* Fixed a bug in Django 5.0 that caused a migration crash on PostgreSQL 15+
+  when adding a partial ``UniqueConstraint`` with ``nulls_distinct``
+  (:ticket:`35329`).

+ 32 - 0
tests/schema/tests.py

@@ -3596,6 +3596,38 @@ class SchemaTests(TransactionTestCase):
         constraints = self.get_constraints(Author._meta.db_table)
         self.assertNotIn(constraint.name, constraints)
 
+    @skipUnlessDBFeature(
+        "supports_nulls_distinct_unique_constraints",
+        "supports_partial_indexes",
+    )
+    def test_unique_constraint_nulls_distinct_condition(self):
+        with connection.schema_editor() as editor:
+            editor.create_model(Author)
+        constraint = UniqueConstraint(
+            fields=["height", "weight"],
+            name="un_height_weight_start_A",
+            condition=Q(name__startswith="A"),
+            nulls_distinct=False,
+        )
+        with connection.schema_editor() as editor:
+            editor.add_constraint(Author, constraint)
+        Author.objects.create(name="Adam", height=None, weight=None)
+        Author.objects.create(name="Avocado", height=1, weight=None)
+        Author.objects.create(name="Adrian", height=None, weight=1)
+        with self.assertRaises(IntegrityError):
+            Author.objects.create(name="Alex", height=None, weight=None)
+        Author.objects.create(name="Bob", height=None, weight=None)
+        with self.assertRaises(IntegrityError):
+            Author.objects.create(name="Alex", height=1, weight=None)
+        Author.objects.create(name="Bill", height=None, weight=None)
+        with self.assertRaises(IntegrityError):
+            Author.objects.create(name="Alex", height=None, weight=1)
+        Author.objects.create(name="Celine", height=None, weight=1)
+        with connection.schema_editor() as editor:
+            editor.remove_constraint(Author, constraint)
+        constraints = self.get_constraints(Author._meta.db_table)
+        self.assertNotIn(constraint.name, constraints)
+
     @skipIfDBFeature("supports_nulls_distinct_unique_constraints")
     def test_unique_constraint_nulls_distinct_unsupported(self):
         # UniqueConstraint is ignored on databases that don't support