Opened 3 months ago

Closed 3 months ago

#36442 closed Bug (fixed)

Reusing same instance of FilteredRelations in multiple queries result in django.core.exceptions.FieldError: Cannot resolve keyword

Reported by: Viliam Mihálik Owned by: Viliam Mihálik
Component: Database layer (models, ORM) Version: 5.0
Severity: Normal Keywords: FilteredRelation, ORM
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Example test case that should pass, used in FilteredRelationTests

def test_reuse_same_filtered_relation(self):
    borrower = Borrower.objects.create(name="Jenny")
    Reservation.objects.create(
        borrower=borrower,
        book=self.book1,
        state=Reservation.STOPPED,
    )
    condition = Q(book__reservation__state=Reservation.STOPPED)
    my_reserved_books = FilteredRelation(
        "book__reservation",
        condition=condition
    )
    first_result = list(Author.objects.annotate(
        my_reserved_books=my_reserved_books,
    ))
    self.assertEqual(my_reserved_books.condition, condition)
    # AssertionError: <Q: (AND: ('my_reserved_books__state', 'stopped'))> != <Q: (AND: ('book__reservation__state', 'stopped'))>
    second_result = list(Author.objects.annotate(
        my_reserved_books=my_reserved_books,
    ))
    # django.core.exceptions.FieldError: Cannot resolve keyword 'my_reserved_books' into field. Choices are: book, content_object, content_type, content_type_id, favorite_books, id, name, object_id

When you reuse same FilteredRelation in different queries, internal FilteredRelation.condition is changed and no longer works.
This same code works on Django 4.x. This change was introduced by fixing of bug #33766

Based on discussion in #33766 it was intended but breaking for us after upgrading from 4 -> 5.
In order to get this working is to my_reserved_books.clone() clone annotate before using.

Change History (6)

comment:1 by Simon Charette, 3 months ago

Triage Stage: UnreviewedAccepted
Type: UncategorizedBug
Version: 5.25.0

We should definitely clone the filtered relation on resolving, the bug is present since 5.0 though so it doesn't qualify for a backport.

Would you be interested in submitting a patch here? I suspect adjusting FilteredRelation.clone to make sure it passes condition=self.condition.clone() should do?

comment:2 by Viliam Mihálik, 3 months ago

I can look at it.
I checked the code, and after debug, .clone method is never called. So my suggestion is to clone annotation before "rename" here
https://github.com/django/django/blob/main/django/db/models/query.py#L1728

and simply change clone.query.add_filtered_relation(annotation, alias) to clone.query.add_filtered_relation(annotation.clone(), alias)

comment:3 by Viliam Mihálik, 3 months ago

Has patch: set

comment:4 by Sarah Boyce, 3 months ago

Owner: set to Viliam Mihálik
Status: newassigned

comment:5 by Sarah Boyce, 3 months ago

Triage Stage: AcceptedReady for checkin

comment:6 by Sarah Boyce <42296566+sarahboyce@…>, 3 months ago

Resolution: fixed
Status: assignedclosed

In bd65e828:

Fixed #36442 -- Cloned FilteredRelation before rename_prefix_from_q.

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