diff --git a/django/db/backends/__init__.py b/django/db/backends/__init__.py index 5c34c3b92a1..ffb58fceba3 100644 --- a/django/db/backends/__init__.py +++ b/django/db/backends/__init__.py @@ -567,9 +567,14 @@ class BaseDatabaseFeatures(object): # Does the backend distinguish between '' and None? interprets_empty_strings_as_nulls = False + # Does the backend allow inserting duplicate NULL rows in a nullable + # unique field? All core backends implement this correctly, but other + # databases such as SQL Server do not. + supports_nullable_unique_constraints = True + # Does the backend allow inserting duplicate rows when a unique_together - # constraint exists, but one of the unique_together columns is NULL? - ignores_nulls_in_unique_constraints = True + # constraint exists and some fields are nullable but not all of them? + supports_partially_nullable_unique_constraints = True can_use_chunked_reads = True can_return_id_from_insert = False diff --git a/django/db/backends/oracle/base.py b/django/db/backends/oracle/base.py index e46b4f2db88..e53c8c9fb67 100644 --- a/django/db/backends/oracle/base.py +++ b/django/db/backends/oracle/base.py @@ -107,7 +107,7 @@ class DatabaseFeatures(BaseDatabaseFeatures): has_zoneinfo_database = pytz is not None supports_bitwise_or = False can_defer_constraint_checks = True - ignores_nulls_in_unique_constraints = False + supports_partially_nullable_unique_constraints = False has_bulk_insert = True supports_tablespaces = True supports_sequence_reset = False diff --git a/tests/distinct_on_fields/tests.py b/tests/distinct_on_fields/tests.py index 33ddf80ff43..3e6e6c23581 100644 --- a/tests/distinct_on_fields/tests.py +++ b/tests/distinct_on_fields/tests.py @@ -8,6 +8,7 @@ from .models import Tag, Celebrity, Fan, Staff, StaffTag @skipUnlessDBFeature('can_distinct_on_fields') +@skipUnlessDBFeature('supports_nullable_unique_constraints') class DistinctOnTests(TestCase): def setUp(self): t1 = Tag.objects.create(name='t1') diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index 38d1260de0b..9a88ff60f06 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -660,7 +660,7 @@ class UniqueTest(TestCase): form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) self.assertTrue(form.is_valid()) - @skipUnlessDBFeature('ignores_nulls_in_unique_constraints') + @skipUnlessDBFeature('supports_nullable_unique_constraints') def test_unique_null(self): title = 'I May Be Wrong But I Doubt It' form = BookForm({'title': title, 'author': self.writer.pk}) diff --git a/tests/model_formsets/tests.py b/tests/model_formsets/tests.py index c1e01cd6bee..85ef430b932 100644 --- a/tests/model_formsets/tests.py +++ b/tests/model_formsets/tests.py @@ -661,7 +661,7 @@ class ModelFormsetTest(TestCase): self.assertEqual(book1.title, 'Flowers of Evil') self.assertEqual(book1.notes, 'English translation of Les Fleurs du Mal') - @skipUnlessDBFeature('ignores_nulls_in_unique_constraints') + @skipUnlessDBFeature('supports_partially_nullable_unique_constraints') def test_inline_formsets_with_nullable_unique_together(self): # Test inline formsets where the inline-edited object has a # unique_together constraint with a nullable member diff --git a/tests/one_to_one_regress/tests.py b/tests/one_to_one_regress/tests.py index 17528de3115..f14d1eeabd6 100644 --- a/tests/one_to_one_regress/tests.py +++ b/tests/one_to_one_regress/tests.py @@ -229,7 +229,7 @@ class OneToOneRegressionTests(TestCase): # Several instances of the origin are only possible if database allows # inserting multiple NULL rows for a unique constraint - if connection.features.ignores_nulls_in_unique_constraints: + if connection.features.supports_nullable_unique_constraints: UndergroundBar.objects.create() # When there are several instances of the origin