#36611: Model validation of constraint involving ForeignObject considers only
first
column
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Type: Bug
Status: new | Component: Database
| layer (models, ORM)
Version: 5.2 | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Discovered during #36580 in
[https://github.com/django/django/pull/19798/files#r2350234373 review].
Similar to #36431, where only the first column of a `ForeignObject` was
considered in `values()`, only the first column is considered during model
validation of constraints.
Composite PK's are not affected, because they raise system checks if you
try to use them in a constraint. But `ForeignObject` has been broken since
its introduction in this regard. Reproduced on 5.2, but thus, not a
release blocker.
Rough test (needs adjusting to avoid hijacking this model and
unnecessarily skipping tests on backends not supporting constraints):
{{{#!diff
diff --git a/tests/composite_pk/models/tenant.py
b/tests/composite_pk/models/tenant.py
index 65eb0feae8..c818ec4de7 100644
--- a/tests/composite_pk/models/tenant.py
+++ b/tests/composite_pk/models/tenant.py
@@ -48,6 +48,16 @@ class Comment(models.Model):
text = models.TextField(default="", blank=True)
integer = models.IntegerField(default=0)
+ class Meta:
+ # TODO: use new model instead
+ required_db_features = {"supports_table_check_constraints"}
+ constraints = [
+ models.CheckConstraint(
+ condition=models.Q(user__lt=(1000, 1000)),
+ name="user_limit",
+ ),
+ ]
+
class Post(models.Model):
pk = models.CompositePrimaryKey("tenant_id", "id")
diff --git a/tests/composite_pk/test_models.py
b/tests/composite_pk/test_models.py
index 27157a52ad..05aafd5306 100644
--- a/tests/composite_pk/test_models.py
+++ b/tests/composite_pk/test_models.py
@@ -1,6 +1,8 @@
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
+from django.db import connection
from django.test import TestCase
+from django.test.utils import CaptureQueriesContext
from .models import Comment, Tenant, Token, User
@@ -119,7 +121,23 @@ class CompositePKModelsTests(TestCase):
self.assertSequenceEqual(ctx.exception.messages,
messages)
def test_full_clean_update(self):
- with self.assertNumQueries(1):
+ with CaptureQueriesContext(connection) as ctx:
+ self.comment_1.full_clean()
+ select_queries = [
+ query["sql"]
+ for query in ctx.captured_queries
+ if "select" in query["sql"].lower()
+ ]
+ self.assertEqual(len(select_queries), 2, select_queries) # 1 on
5.2.x
+
+ def test_full_clean_update_invalid(self):
+ self.comment_1.tenant_id = 1001
+ with self.assertRaises(ValidationError):
+ self.comment_1.full_clean()
+
+ self.comment_1.tenant_id = 1
+ self.comment_1.user_id = 1001
+ with self.assertRaises(ValidationError):
self.comment_1.full_clean()
def test_field_conflicts(self):
diff --git a/tests/composite_pk/tests.py b/tests/composite_pk/tests.py
index 2245a472e4..5b7e34a0bc 100644
--- a/tests/composite_pk/tests.py
+++ b/tests/composite_pk/tests.py
@@ -187,12 +187,17 @@ class CompositePKTests(TestCase):
self.assertEqual(user.email, self.user.email)
def test_select_related(self):
- Comment.objects.create(tenant=self.tenant, id=2)
+ user2 = User.objects.create(
+ tenant=self.tenant,
+ id=2,
+ email="[email protected]",
+ )
+ Comment.objects.create(tenant=self.tenant, id=2, user=user2)
with self.assertNumQueries(1):
comments =
list(Comment.objects.select_related("user").order_by("pk"))
self.assertEqual(len(comments), 2)
self.assertEqual(comments[0].user, self.user)
- self.assertIsNone(comments[1].user)
+ self.assertEqual(comments[1].user, user2)
def test_model_forms(self):
fields = ["tenant", "id", "user_id", "text", "integer"]
}}}
----
Notice `1001` only appears in the first query of the
`test_full_clean_update_invalid`.
{{{#!py
FAIL: test_full_clean_update_invalid
(composite_pk.test_models.CompositePKModelsTests.test_full_clean_update_invalid)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/jwalls/django/tests/composite_pk/test_models.py", line 140,
in test_full_clean_update_invalid
with self.assertRaises(ValidationError):
~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
AssertionError: ValidationError not raised
----------------------------------------------------------------------
(0.000)
SELECT 1 AS "a"
FROM "composite_pk_tenant"
WHERE "composite_pk_tenant"."id" = 1001
LIMIT 1;
args=(1,
1001);
ALIAS=DEFAULT (0.000)
SELECT 1 AS "a"
FROM "composite_pk_tenant"
WHERE "composite_pk_tenant"."id" = 1
LIMIT 1;
args=(1,
1);
ALIAS=DEFAULT
----------------------------------------------------------------------
Ran 2 tests in 0.003s
FAILED (failures=1)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36611>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019950633331-91edb4d2-1c33-4ad0-9288-71fbbd939cc1-000000%40eu-central-1.amazonses.com.