#32635 closed Bug (fixed)
System checks for invalid model field names in CheckConstraint.check/UniqueConstraint.condition crash with a reverse 020 relation.
Reported by: | sim1234 | Owned by: | Hasan Ramezani |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 3.2 |
Severity: | Release blocker | Keywords: | UniqueConstraint OneToOneField |
Cc: | Hasan Ramezani | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Creating a UniqueConstraint with condition referencing OneToOneField generates invalid SQL
Example:
# models.py from django.db import models class Model1(models.Model): name = models.CharField(max_length=255) class Meta: constraints = ( models.UniqueConstraint( name="unique_onetoone", fields=("name", ), condition=models.Q(model__isnull=True), ), ) class Model2(models.Model): name = models.CharField(max_length=255) model = models.OneToOneField( Model1, related_name="model", on_delete=models.CASCADE ) # generated migrations/0001_initial.py import django.db.models.deletion from django.db import migrations, models class Migration(migrations.Migration): dependencies = [] operations = [ // ... CreateModel, etc ... migrations.AddConstraint( model_name="model1", constraint=models.UniqueConstraint( name="unique_onetoone", fields=("name", ), condition=models.Q(model__isnull=True), ), ), ]
Running manage.py sqlmigrate test 0001 generates this SQL for creating the unique index and the WHERE clause is completly wrong.
-- ... CREATE TABLE, etc ... CREATE UNIQUE INDEX "unique_onetoone" ON "test_model1" ("name", ) WHERE "id" IS NULL;
Expected behaviour is receiving FieldError("Joined field references are not permitted in this query"). This behaviour is observed when using condition=models.Q(modelidisnull=True).
Change History (7)
comment:1 by , 4 years ago
Component: | Uncategorized → Database layer (models, ORM) |
---|---|
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
comment:2 by , 4 years ago
Cc: | added |
---|---|
Severity: | Normal → Release blocker |
Summary: | Creating a UniqueConstraint with condition referencing OneToOneField generates invalid SQL → System checks for invalid model field names in CheckConstraint.check/UniqueConstraint.condition crash with a reverse 020 relation. |
Version: | 2.2 → 3.2 |
System checks for invalid model field names in CheckConstraint.check
and UniqueConstraint.condition
, added in b7b7df5fbcf44e6598396905136cab5a19e9faff, crash for me with a reverse 020 relation, e.g.
diff --git a/tests/invalid_models_tests/test_models.py b/tests/invalid_models_tests/test_models.py index c79684487d..f39e424251 100644 --- a/tests/invalid_models_tests/test_models.py +++ b/tests/invalid_models_tests/test_models.py @@ -1694,6 +1694,25 @@ 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, related_name='model') + + 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', + ), + ]) +
File "django/django/db/models/base.py", line 1296, in check *cls._check_constraints(databases), File "django/django/db/models/base.py", line 2108, in _check_constraints field.get_transform(first_lookup) is None and AttributeError: 'OneToOneRel' object has no attribute 'get_transform'
Marking as a release blocker since it's a bug in a new feature.
comment:3 by , 4 years ago
Has patch: | set |
---|---|
Owner: | changed from | to
Status: | new → assigned |
comment:5 by , 4 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
I could swear this was discussed already but both
conditions
andexpressions
forIndex
andConstrant
should be checked. This should be achievable through the useallow_joins=False
at resolving time.