#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="user0...@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"]
 }}}

 ... 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 django-updates+unsubscr...@googlegroups.com.
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