#36682: RecursionError when traversing several reverse relations with
FilteredRelation
-------------------------------------+-------------------------------------
Reporter: REGNIER Guillaume | Type: Bug
Status: new | Component: Database
| layer (models, ORM)
Version: 5.2 | Severity: Normal
Keywords: FilteredRelation | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
When declaring a filtered relation with a condition on the 2-distant
reverse relation, query compilation fails with a RecursionError
=== Steps to Reproduce:
{{{#!python
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()
}}}
{{{#!python
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
{{{#!sql
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 `Query.join()` fails to reuse the join for 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:
{{{#!python
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
--
Ticket URL: <https://code.djangoproject.com/ticket/36682>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion visit
https://groups.google.com/d/msgid/django-updates/0107019a107daba8-c844a47d-9ed6-4323-99cc-12c39d23a514-000000%40eu-central-1.amazonses.com.