summaryrefslogtreecommitdiff
path: root/django/db/models/query_utils.py
diff options
context:
space:
mode:
authorSimon Charette <charette.s@gmail.com>2023-04-20 00:25:57 -0400
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-04-24 08:32:44 +0200
commitd660cee5bc68b597503c2a16f3d9928d52f93fb4 (patch)
tree7d809a0fa4e5d49d45f5c09252fb971a7463701f /django/db/models/query_utils.py
parent3fe0c609cf6d50e45e1246492ebac02660561177 (diff)
downloaddjango-d660cee5bc68b597503c2a16f3d9928d52f93fb4.tar.gz
Fixed #33766 -- Resolved FilteredRelation.condition at referencing time.
The previous implementation resolved condition at Join compilation time which required introducing a specialized expression resolving mode to alter the join reuse logic solely during that phase. FilteredRelation.condition is now resolved when the relation is first referenced which maintains the existing behavior while allowing the removal of the specialized resolving mode and address an issue where conditions couldn't spawn new joins.
Diffstat (limited to 'django/db/models/query_utils.py')
-rw-r--r--django/db/models/query_utils.py31
1 files changed, 19 insertions, 12 deletions
diff --git a/django/db/models/query_utils.py b/django/db/models/query_utils.py
index a82ed23dbb..52b472d252 100644
--- a/django/db/models/query_utils.py
+++ b/django/db/models/query_utils.py
@@ -403,8 +403,11 @@ class FilteredRelation:
self.alias = None
if not isinstance(condition, Q):
raise ValueError("condition argument must be a Q() instance.")
+ # .condition and .resolved_condition have to be stored independently
+ # as the former must remain unchanged for Join.__eq__ to remain stable
+ # and reusable even once their .filtered_relation are resolved.
self.condition = condition
- self.path = []
+ self.resolved_condition = None
def __eq__(self, other):
if not isinstance(other, self.__class__):
@@ -418,18 +421,22 @@ class FilteredRelation:
def clone(self):
clone = FilteredRelation(self.relation_name, condition=self.condition)
clone.alias = self.alias
- clone.path = self.path[:]
+ if (resolved_condition := self.resolved_condition) is not None:
+ clone.resolved_condition = resolved_condition.clone()
return clone
- def resolve_expression(self, *args, **kwargs):
- """
- QuerySet.annotate() only accepts expression-like arguments
- (with a resolve_expression() method).
- """
- raise NotImplementedError("FilteredRelation.resolve_expression() is unused.")
+ def relabeled_clone(self, change_map):
+ clone = self.clone()
+ if resolved_condition := clone.resolved_condition:
+ clone.resolved_condition = resolved_condition.relabeled_clone(change_map)
+ return clone
+
+ def resolve_expression(self, query, reuse, *args, **kwargs):
+ clone = self.clone()
+ clone.resolved_condition = query.build_filtered_relation_q(
+ self.condition, reuse=reuse
+ )
+ return clone
def as_sql(self, compiler, connection):
- # Resolve the condition in Join.filtered_relation.
- query = compiler.query
- where = query.build_filtered_relation_q(self.condition, reuse=set(self.path))
- return compiler.compile(where)
+ return compiler.compile(self.resolved_condition)