#34803: Nested OuterRef can raise AttributeError: 'OuterRef' object has no
attribute 'contains_over_clause'
-------------------------------------+-------------------------------------
               Reporter:             |          Owner:  nobody
  pierrenicolasr                     |
                   Type:  Bug        |         Status:  new
              Component:  Database   |        Version:  4.2
  layer (models, ORM)                |
               Severity:  Normal     |       Keywords:
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  0
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 Porting our application from Django 3 to 4, we're seeing exception raised
 in complex queries using nested OuterRef


 {{{
 File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/query.py", line 1436, in filter
     return self._filter_or_exclude(False, args, kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/query.py", line 1454, in _filter_or_exclude
     clone._filter_or_exclude_inplace(negate, args, kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/query.py", line 1461, in
 _filter_or_exclude_inplace
     self._query.add_q(Q(*args, **kwargs))
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/query.py", line 1545, in add_q
     clause, _ = self._add_q(q_object, self.used_aliases)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/query.py", line 1576, in _add_q
     child_clause, needed_inner = self.build_filter(
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/query.py", line 1435, in build_filter
     value = self.resolve_lookup_value(value, can_reuse, allow_joins)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/query.py", line 1204, in
 resolve_lookup_value
     value = value.resolve_expression(
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/query.py", line 1923, in resolve_expression
     query = self.query.resolve_expression(*args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/query.py", line 1145, in resolve_expression
     clone.where.resolve_expression(query, *args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/where.py", line 278, in resolve_expression
     clone._resolve_node(clone, *args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/where.py", line 270, in _resolve_node
     cls._resolve_node(child, query, *args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/where.py", line 274, in _resolve_node
     node.rhs = cls._resolve_leaf(node.rhs, query, *args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/site-
 packages/django/db/models/sql/where.py", line 263, in _resolve_leaf
     expr = expr.resolve_expression(query, *args, **kwargs)
   File "/usr/local/filewave/python/lib/python3.10/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'
 }}}

 Looking at code, the issue seems to be the following:
 {{{
 class ResolvedOuterRef(F):
     """
     An object that contains a reference to an outer query.

     In this case, the reference to the outer query has been resolved
 because
     the inner query has been used as a subquery.
     """

     contains_aggregate = False
     contains_over_clause = False

     def as_sql(self, *args, **kwargs):
         raise ValueError(
             "This queryset contains a reference to an outer query and may
 "
             "only be used in a subquery."
         )

     def resolve_expression(self, *args, **kwargs):
         col = super().resolve_expression(*args, **kwargs)
         if col.contains_over_clause:
             raise NotSupportedError(
                 f"Referencing outer query window expression is not
 supported: "
                 f"{self.name}."
             )
 }}}

 In case of OuterRef(OuterRef( "field" )), `col =
 super().resolve_expression(*args, **kwargs)` will use:
 {{{
 class OuterRef(F):
     contains_aggregate = False


     def resolve_expression(self, *args, **kwargs):
         if isinstance(self.name, self.__class__):
             return self.name
         return ResolvedOuterRef(self.name)
 }}}
 so self.name is an OuterRef, then it returns it directly, and then ` if
 col.contains_over_clause:` fails because col.contains_over_clause is not
 defined for OuterRef.

 Looks like adding col.contains_over_clause=False to class OuterRef solves
 the issue - or checking if object col.contains_over_clause.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/34803>
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 on the web visit 
https://groups.google.com/d/msgid/django-updates/0107018a416556d4-51a6cd87-8ec6-4c3c-b7b3-fb8d8e7224b2-000000%40eu-central-1.amazonses.com.

Reply via email to