From a77c9a4229cfef790ec18001b2cd18bd9c4aedbc Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 13 Apr 2021 11:51:19 +0200 Subject: [PATCH] Fixed #32635 -- Fixed system check crash for reverse o2o relations in CheckConstraint.check and UniqueConstraint.condition. Regression in b7b7df5fbcf44e6598396905136cab5a19e9faff. Thanks Szymon Zmilczak for the report. --- django/db/models/base.py | 2 ++ docs/releases/3.2.1.txt | 4 +++ tests/invalid_models_tests/test_models.py | 43 +++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/django/db/models/base.py b/django/db/models/base.py index 55b1691166..cd5268bed1 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -2105,6 +2105,8 @@ class Model(metaclass=ModelBase): # JOIN must happen at the first lookup. first_lookup = lookups[0] if ( + hasattr(field, 'get_transform') and + hasattr(field, 'get_lookup') and field.get_transform(first_lookup) is None and field.get_lookup(first_lookup) is None ): diff --git a/docs/releases/3.2.1.txt b/docs/releases/3.2.1.txt index 376efb0211..cc716782e5 100644 --- a/docs/releases/3.2.1.txt +++ b/docs/releases/3.2.1.txt @@ -22,3 +22,7 @@ Bugfixes * Restored, following a regression in Django 3.2, displaying an exception message on the technical 404 debug page (:ticket:`32637`). + +* Fixed a bug in Django 3.2 where a system check would crash on a reverse + one-to-one relationships in ``CheckConstraint.check`` or + ``UniqueConstraint.condition`` (:ticket:`32635`). diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py index 6c3017ff47..958ad120ca 100644 --- a/tests/invalid_models_tests/test_models.py +++ b/tests/invalid_models_tests/test_models.py @@ -1694,6 +1694,27 @@ class ConstraintsTests(TestCase): ), ]) + @skipUnlessDBFeature('supports_table_check_constraints') + def test_check_constraint_pointing_to_reverse_o2o(self): + class Model(models.Model): + parent = models.OneToOneField('self', models.CASCADE) + + class Meta: + constraints = [ + models.CheckConstraint( + name='name', + check=models.Q(model__isnull=True), + ), + ] + + self.assertEqual(Model.check(databases=self.databases), [ + Error( + "'constraints' refers to the nonexistent field 'model'.", + obj=Model, + id='models.E012', + ), + ]) + @skipUnlessDBFeature('supports_table_check_constraints') def test_check_constraint_pointing_to_m2m_field(self): class Model(models.Model): @@ -1943,6 +1964,28 @@ class ConstraintsTests(TestCase): ) ] if connection.features.supports_partial_indexes else []) + def test_unique_constraint_pointing_to_reverse_o2o(self): + class Model(models.Model): + parent = models.OneToOneField('self', models.CASCADE) + + class Meta: + required_db_features = {'supports_partial_indexes'} + constraints = [ + models.UniqueConstraint( + fields=['parent'], + name='name', + condition=models.Q(model__isnull=True), + ), + ] + + self.assertEqual(Model.check(databases=self.databases), [ + Error( + "'constraints' refers to the nonexistent field 'model'.", + obj=Model, + id='models.E012', + ), + ] if connection.features.supports_partial_indexes else []) + def test_deferrable_unique_constraint(self): class Model(models.Model): age = models.IntegerField()