Opened 5 years ago

Closed 5 years ago

#30739 closed Bug (fixed)

OuterRef in exclude() or ~Q() uses wrong model.

Reported by: Oskar Persson Owned by: Simon Charette
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: no UI/UX: no

Description

The following test (added to tests/queries/test_qs_combinators) fails when trying to exclude results using OuterRef()

def test_exists_exclude(self):
    # filter()
    qs = Number.objects.annotate(
        foo=Exists(
            Item.objects.filter(tags__category_id=OuterRef('pk'))
        )
    ).filter(foo=True)
    print(qs)  # works

    # exclude()
    qs = Number.objects.annotate(
        foo =Exists(
            Item.objects.exclude(tags__category_id=OuterRef('pk'))
        )
    ).filter(foo=True)
    print(qs)  # crashes

    # filter(~Q())
    qs = Number.objects.annotate(
        foo =Exists(
            Item.objects.filter(~Q(tags__category_id=OuterRef('pk')))
        )
    ).filter(foo=True)
    print(qs)  # crashes

It results in the following error

ValueError: This queryset contains a reference to an outer query and may only be used in a subquery

Change History (4)

comment:1 by Mariusz Felisiak, 5 years ago

Cc: Simon Charette added
Summary: OuterRef does not work with exclude() or ~Q()OuterRef in exclude() or ~Q() uses wrong model.
Triage Stage: UnreviewedAccepted
Version: 2.2master

Thanks for the report. ValueError was fixed in 35431298226165986ad07e91f9d3aca721ff38ec. Provided test fails on master with

django.db.utils.ProgrammingError: missing FROM-clause entry for table "V0"
LINE 1: ...(U1."tag_id" = U2."id") WHERE U2."category_id" = ("V0"."id")...

It looks that OuterRef resolves to "V0"."id" instead of "queries_number"."id"

SELECT ...
FROM "queries_number" 
WHERE EXISTS(
    SELECT ...
    FROM "queries_item" V0
    WHERE NOT (V0."id" IN (
        SELECT U1."item_id"
        FROM "queries_item_tags" U1 INNER JOIN "queries_tag" U2 ON (U1."tag_id" = U2."id")
        WHERE U2."category_id" = "V0"."id"
    ))

Issue can be reproduce also without annotation

Number.objects.filter(Exists(Item.objects.exclude(tags__category_id=OuterRef('pk'))))

comment:2 by Simon Charette, 5 years ago

Owner: changed from nobody to Simon Charette
Status: newassigned

comment:3 by Simon Charette, 5 years ago

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

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

Resolution: fixed
Status: assignedclosed

In 13a8884a:

Fixed #30739 -- Fixed exclusion of multi-valued lookup against outer rhs.

OuterRef right hand sides have to be nested, just like F rhs have to,
during the subquery pushdown split_exclude performs to ensure they are
resolved against the outer query aliases.

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