#34803: Nested OuterRef can raise AttributeError: 'OuterRef' object has no
attribute 'contains_over_clause'
-------------------------------------+-------------------------------------
     Reporter:  Pierre-Nicolas       |                    Owner:  nobody
  Rigal                              |
         Type:  Bug                  |                   Status:  new
    Component:  Database layer       |                  Version:  4.2
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:                       |             Triage Stage:
                                     |  Unreviewed
    Has patch:  0                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by Pierre-Nicolas Rigal):

 * status:  closed => new
 * resolution:  worksforme =>


Comment:

 Obviously not real life example, but reproduces the issue:
 {{{
 from django.db import models


 class A(models.Model):
     key = models.IntegerField()


 class B(models.Model):
     a = models.ForeignKey(A, on_delete=models.CASCADE)


 class C(models.Model):
     b = models.ForeignKey(B, on_delete=models.CASCADE)


 class D(models.Model):
     c = models.ForeignKey(C, on_delete=models.CASCADE)
     key = models.IntegerField()
 }}}
 and test is:
 {{{
 from django.db.models import Exists, OuterRef
 from django.test import TestCase

 from bg34803.models import B, A, C, D


 class TestOuterRef(TestCase):
     def test_nested_annotated_outerref(self):
         query = (
             A.objects
             .filter(Exists(
                 B.objects
                 .filter(Exists(
                     C.objects
                     .annotate(a_key=OuterRef(OuterRef('key')))
                     .filter(Exists(
                         D.objects
                         .filter(key=OuterRef('a_key'))
                     ))
                 ))
             ))
         )
 }}}

 result:
 {{{
 $ python manage.py test
 Found 1 test(s).
 Creating test database for alias 'default'...
 System check identified no issues (0 silenced).
 E
 ======================================================================
 ERROR: test_nested_annotated_outerref (bg34803.tests.TestOuterRef)
 ----------------------------------------------------------------------
 Traceback (most recent call last):
   File "/Users/pnr/Desktop/django_issue/bug34803/bg34803/tests.py", line
 14, in test_nested_annotated_outerref
     C.objects
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/query.py", line 1436, in filter
     return self._filter_or_exclude(False, args, kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/query.py", line 1454, in _filter_or_exclude
     clone._filter_or_exclude_inplace(negate, args, kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/query.py", line 1461, in
 _filter_or_exclude_inplace
     self._query.add_q(Q(*args, **kwargs))
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/query.py", line 1545, in add_q
     clause, _ = self._add_q(q_object, self.used_aliases)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/query.py", line 1576, in _add_q
     child_clause, needed_inner = self.build_filter(
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/query.py", line 1417, in build_filter
     condition = filter_expr.resolve_expression(
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/expressions.py", line 278, in resolve_expression
     [
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/expressions.py", line 279, in <listcomp>
     expr.resolve_expression(query, allow_joins, reuse, summarize)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/query.py", line 1145, in resolve_expression
     clone.where.resolve_expression(query, *args, **kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/where.py", line 278, in resolve_expression
     clone._resolve_node(clone, *args, **kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/where.py", line 270, in _resolve_node
     cls._resolve_node(child, query, *args, **kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/where.py", line 274, in _resolve_node
     node.rhs = cls._resolve_leaf(node.rhs, query, *args, **kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/sql/where.py", line 263, in _resolve_leaf
     expr = expr.resolve_expression(query, *args, **kwargs)
   File "/Users/pnr/Desktop/django_issue/.env/lib/python3.9/site-
 packages/django/db/models/expressions.py", line 862, in resolve_expression
     if col.contains_over_clause:
 AttributeError: 'OuterRef' object has no attribute 'contains_over_clause'

 ----------------------------------------------------------------------
 }}}

 We seem to have a fix by simplifying the query we use, but still wanted to
 report as this is a behavior change with Django 3.
 Thanks.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34803#comment:5>
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 on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018a4647af86-567eddd7-844b-4b19-8846-6f8053a518c7-000000%40eu-central-1.amazonses.com.

Reply via email to