Opened 3 weeks ago

Last modified 2 weeks ago

#36682 assigned Bug

RecursionError when traversing several reverse relations with FilteredRelation

Reported by: REGNIER Guillaume Owned by: Augusto Pontes
Component: Database layer (models, ORM) Version: 5.2
Severity: Normal Keywords: FilteredRelation
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description (last modified by REGNIER Guillaume)

When declaring a filtered relation with a condition on the 2-distant reverse relation, query compilation fails with a RecursionError

Steps to Reproduce:

class Company(models.Model):
    pass

class Employee(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE, related_name="employees")

class Mission(models.Model):
    employee = models.ForeignKey(Employee, on_delete=models.CASCADE, related_name="missions")
    name = models.TextField()
    deadline = models.DateTimeField()
qs = Company.objects.alias(
    specific_mission=FilteredRelation(
        "employees__missions",
        condition=Q(employees__missions__name="specific"),
    )
)
qs = qs.filter(specific_mission__deadline__lt=localtime())
list(qs)

Expected Behavior:

.filter(...) does not throw RecursionError and produce an SQL query equivalent to

SELECT 
  "company"."id" 
FROM 
  "company" 
  INNER JOIN "employee" ON (
    "company"."id" = "employee"."company_id"
  ) 
  INNER JOIN "mission" specific_mission ON (
    "employee"."id" = specific_mission."employee_id" 
    AND specific_mission."name" = 'specific'
  ) 
WHERE 
  specific_mission."deadline" < '2025-10-23 11:07:06.224821+02:00'

Actual Behavior:

.filter(...) throws RecursionError

Analysis:

My understanding is that Query.join() fails to reuse the join for the first reverse relation, leading to the FilteredRelation being resolved again and again.
Loop is:

  • FilteredRelation.resolve_expression()
  • Query.build_filter()
  • Query._add_q()
  • Query.build_filter()
  • Query.setup_joins()
  • Query.join()
  • FilteredRelation.resolve_expression()

Workaround:

        qs = Company.objects.alias(
            _employees=FilteredRelation("employees"),
            specific_mission=FilteredRelation(
                "_employees__missions",
                condition=Q(_employees__missions__name="specific"),
            ),
        )
        qs = qs.filter(specific_mission__deadline__lt=localtime())
        list(qs)

Related tickets:

#36109: Related to FilteredRelation and RecursionError (uses mentioned workaround)
#34957: Probably the same underlying issue, ticket was closed due to lacks reproduction details

Change History (9)

comment:1 by Simon Charette, 3 weeks ago

Triage Stage: UnreviewedAccepted

Thank you for your report, reproduced against 5e2bbebed9f36bb9d15f168444e7982287761877.

Last edited 3 weeks ago by Simon Charette (previous) (diff)

comment:2 by REGNIER Guillaume, 3 weeks ago

Description: modified (diff)

comment:3 by Augusto Pontes, 2 weeks ago

Has patch: set
Needs documentation: set
Needs tests: set
Owner: set to Augusto Pontes
Status: newassigned

comment:4 by Augusto Pontes, 2 weeks ago

Hi, so i have a suggestion of how fixing it, i sent a PR, have a look:

https://github.com/django/django/pull/20010

comment:5 by Augusto Pontes, 2 weeks ago

Triage Stage: AcceptedReady for checkin

comment:6 by Simon Charette, 2 weeks ago

Needs documentation: unset
Patch needs improvement: set
Triage Stage: Ready for checkinAccepted

You can't mark your own patch RFC.

More over the proposed patch has several problems that should be addressed before it can be considered for final review.

in reply to:  6 comment:7 by Augusto Pontes, 2 weeks ago

Replying to Simon Charette:

You can't mark your own patch RFC.

More over the proposed patch has several problems that should be addressed before it can be considered for final review.

Ok

in reply to:  6 comment:8 by Augusto Pontes, 2 weeks ago

Replying to Simon Charette:

You can't mark your own patch RFC.

More over the proposed patch has several problems that should be addressed before it can be considered for final review.

I accept suggestions on the PR of how can i improve to solve this problem

in reply to:  6 comment:9 by Augusto Pontes, 2 weeks ago

Replying to Simon Charette:

You can't mark your own patch RFC.

More over the proposed patch has several problems that should be addressed before it can be considered for final review.

I saw your comments, i will read them

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