#36580: Model validation of constraints fails if condition's Q object references
ForeignObject
-------------------------------------+-------------------------------------
     Reporter:  Jacob Walls          |                     Type:  Bug
       Status:  new                  |                Component:  Database
                                     |  layer (models, ORM)
      Version:  dev                  |                 Severity:  Release
                                     |  blocker
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
 Similar to #36433, just for `ForeignObject` instead of `ForeignKey`.

 With this adjusted test model and corresponding adjustment to unrelated
 test, `test_full_clean_update` passes on stable/5.2.x (the constraint is
 purposefully not very imaginative, can polish in PR review):
 {{{#!diff
 diff --git a/tests/composite_pk/models/tenant.py
 b/tests/composite_pk/models/tenant.py
 index 65eb0feae8..954a5519f8 100644
 --- a/tests/composite_pk/models/tenant.py
 +++ b/tests/composite_pk/models/tenant.py
 @@ -48,6 +48,14 @@ class Comment(models.Model):
      text = models.TextField(default="", blank=True)
      integer = models.IntegerField(default=0)

 +    class Meta:
 +        constraints = [
 +            models.CheckConstraint(
 +                condition=models.Q(user__isnull=False),
 +                name="user_not_null",
 +            ),
 +        ]
 +

  class Post(models.Model):
      pk = models.CompositePrimaryKey("tenant_id", "id")
 diff --git a/tests/composite_pk/tests.py b/tests/composite_pk/tests.py
 index c4a8e6ca8c..cade405dee 100644
 --- a/tests/composite_pk/tests.py
 +++ b/tests/composite_pk/tests.py
 @@ -205,12 +205,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"]
 }}}

 ... but fails on main:
 {{{#!py
 ======================================================================
 ERROR: test_full_clean_update
 (composite_pk.test_models.CompositePKModelsTests.test_full_clean_update)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File
 
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/unittest/case.py",
 line 58, in testPartExecutor
     yield
   File
 
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/unittest/case.py",
 line 651, in run
     self._callTestMethod(testMethod)

   File
 
"/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/unittest/case.py",
 line 606, in _callTestMethod
     if method() is not None:
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/tests/composite_pk/test_models.py", line 123,
 in test_full_clean_update
     self.comment_1.full_clean()
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/base.py", line 1638, in
 full_clean
     self.validate_constraints(exclude=exclude)
     ^^^^^^^
   File "/Users/jwalls/django/django/db/models/base.py", line 1586, in
 validate_constraints
     constraint.validate(model_class, self, exclude=exclude, using=using)
     ^^^
   File "/Users/jwalls/django/django/db/models/constraints.py", line 212,
 in validate
     if not Q(self.condition).check(against, using=using):
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/query_utils.py", line 176,
 in check
     query.add_q(Q(Coalesce(self, True, output_field=BooleanField())))
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1670, in
 add_q
     clause, _ = self._add_q(q_object, can_reuse)
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1702, in
 _add_q
     child_clause, needed_inner = self.build_filter(
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1541, in
 build_filter
     condition = filter_expr.resolve_expression(
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/expressions.py", line 301,
 in resolve_expression
     expr.resolve_expression(query, allow_joins, reuse, summarize,
 for_save)
     ^^^^^^^
   File "/Users/jwalls/django/django/db/models/query_utils.py", line 91, in
 resolve_expression
     clause, joins = query._add_q(
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1702, in
 _add_q
     child_clause, needed_inner = self.build_filter(
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1527, in
 build_filter
     return self._add_q(
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1702, in
 _add_q
     child_clause, needed_inner = self.build_filter(
     ^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1550, in
 build_filter
     lookups, parts, reffed_expression = self.solve_lookup_type(arg,
 summarize)
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1357, in
 solve_lookup_type
     _, field, _, lookup_parts = self.names_to_path(lookup_splitted,
 self.get_meta())
     ^^^^^^^^^^^^^^^
   File "/Users/jwalls/django/django/db/models/sql/query.py", line 1830, in
 names_to_path
     raise FieldError(
     ^^^
 django.core.exceptions.FieldError: Cannot resolve keyword 'user' into
 field. Choices are: _check

 ----------------------------------------------------------------------
 Ran 178 tests in 1.224s
 }}}

 ---

 There is a prior comment in ticket:36433#comment:1 about improving the
 error message.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36580>
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/01070198f2bc627d-5118a485-1ea3-40bd-bbbe-e35db83c3be7-000000%40eu-central-1.amazonses.com.

Reply via email to