diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index f442d290a005..635793b1e32e 100644 --- a/django/db/backends/base/schema.py +++ b/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" diff --git a/docs/releases/5.0.4.txt b/docs/releases/5.0.4.txt index 9b2fd6b17000..6503f1ada34d 100644 --- a/docs/releases/5.0.4.txt +++ b/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`). diff --git a/tests/schema/tests.py b/tests/schema/tests.py index b912d353eb90..f8e314d2708a 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -3629,6 +3629,38 @@ def test_unique_constraint_nulls_distinct(self): 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