Opened 5 years ago
Closed 5 years ago
#31415 closed Bug (fixed)
QuerySet crashes when nested OuterRef is combined with an operator or used in function.
Reported by: | Ben Nace | Owned by: | Hasan Ramezani |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Simon Charette | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | yes | UI/UX: | no |
Description
Given the following contrived set of models and the latest Django 3.0.5:
class Node(models.Model): steplen = 4 path = models.CharField(max_length=100) class NodeAttribute(models.Model): node = models.ForeignKey(Node, on_delete=models.CASCADE) attribute_name = models.CharField(max_length=64) attribute_value = models.CharField(max_length=64) class Entity(models.Model): name = models.CharField(max_length=64) age = models.IntegerField() class EntityNode(models.Model): entity = models.ForeignKey(Entity, on_delete=models.CASCADE) node = models.ForeignKey(Node, on_delete=models.CASCADE) role = models.CharField(max_length=64)
The query
NodeAttribute.objects.annotate( test=Subquery(Entity.objects.filter( entitynode__node__nodeattribute__attribute_name=OuterRef('attribute_name'), name=Subquery(EntityNode.objects.filter( node__path=Left(OuterRef(OuterRef('node__path')), Node.steplen * 2) ).values('entity__name')[:1]) ).values('age')[:1]) )
produces the exception "AttributeError: 'OuterRef' object has no attribute 'relabeled_clone'."
The problem is with using nested OuterRefs as a parameter to Left. I have not had any trouble with using a single OuterRef with SQL functions, but trying to go up two levels with nested OuterRefs does not appear to work. Please note that the above models and query are for illustrative purposes only and represent the simplest example of the problem I could come up with.
Change History (9)
follow-up: 3 comment:1 by , 5 years ago
comment:2 by , 5 years ago
Cc: | added |
---|
comment:3 by , 5 years ago
Replying to Simon Charette:
Pretty sure this is a duplicate of #29214 (see comment:12). Just like
F
theOuterRef
expression doesn't support__
join references, the factLeft
and nestedOuterRef
are used here are unrelated I believe.
#29214 comment:12 refers to joins inside F in UPDATE queries, not in general. The documentation states
You can also use the double underscore notation to span relationships in an F() object. An F() object with a double underscore will introduce any joins needed to access the related object.
comment:4 by , 5 years ago
Sorry my mistake, I misread the ticket description and thought this was about update
as well.
Could you try simply adding this method to OuteRef
and see if it addresses your crash?
-
django/db/models/expressions.py
diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 84960d77e1..4b24953a3d 100644
a b class OuterRef(F): 581 581 return self.name 582 582 return ResolvedOuterRef(self.name) 583 583 584 def relabeled_clone(self, relabels): 585 return self 586 584 587 585 588 class Func(SQLiteNumericMixin, Expression): 586 589 """An SQL function call."""
comment:5 by , 5 years ago
Easy pickings: | set |
---|---|
Summary: | Nested OuterRefs produce error when used as argument to Left → QuerySet crashes when nested OuterRef is combined with an operator or used in function. |
Triage Stage: | Unreviewed → Accepted |
Type: | Uncategorized → Bug |
Version: | 3.0 → master |
Adding OuterRef.relabeled_clone()
fix this issue for me. It's not related with join references, you can easily reproduce it by combining nested OuterRef()
with an operator, e.g. Time.objects.filter(time__lt=OuterRef(OuterRef('time')) * 2)
.
Related with #29142.
comment:6 by , 5 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
Pretty sure this is a duplicate of #29214 (see comment:12). Just like
F
theOuterRef
expression doesn't support__
join references, the factLeft
and nestedOuterRef
are used here are unrelated I believe.