﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36611	Model validation of constraint involving ForeignObject considers only first column	Jacob Walls	Jacob Walls	"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=""user0002@example.com"",
+        )
+        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)
}}}"	Bug	closed	Database layer (models, ORM)	5.2	Normal	fixed		Samriddha Kumar Tripathi JaeHyuckSa Simon Charette Devendra Tumu	Ready for checkin	1	0	0	0	0	0
