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)

comment:1 by Simon Charette, 5 years ago

Pretty sure this is a duplicate of #29214 (see comment:12). Just like F the OuterRef expression doesn't support __ join references, the fact Left and nested OuterRef are used here are unrelated I believe.

comment:2 by Simon Charette, 5 years ago

Cc: Simon Charette added

in reply to:  1 comment:3 by Ben Nace, 5 years ago

Replying to Simon Charette:

Pretty sure this is a duplicate of #29214 (see comment:12). Just like F the OuterRef expression doesn't support __ join references, the fact Left and nested OuterRef 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 Simon Charette, 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):  
    581581            return self.name
    582582        return ResolvedOuterRef(self.name)
    583583
     584    def relabeled_clone(self, relabels):
     585        return self
     586
    584587
    585588class Func(SQLiteNumericMixin, Expression):
    586589    """An SQL function call."""

comment:5 by Mariusz Felisiak, 5 years ago

Easy pickings: set
Summary: Nested OuterRefs produce error when used as argument to LeftQuerySet crashes when nested OuterRef is combined with an operator or used in function.
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug
Version: 3.0master

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 Hasan Ramezani, 5 years ago

Owner: changed from nobody to Hasan Ramezani
Status: newassigned

comment:7 by Hasan Ramezani, 5 years ago

Has patch: set
Last edited 5 years ago by Mariusz Felisiak (previous) (diff)

comment:8 by Ben Nace, 5 years ago

I can confirm the proposed patch fixes it for me too.

comment:9 by Mariusz Felisiak <felisiak.mariusz@…>, 5 years ago

Resolution: fixed
Status: assignedclosed

In 6fbce45:

Fixed #31415 -- Fixed crash when nested OuterRef is used with operators or in database functions.

Note: See TracTickets for help on using tickets.
Back to Top