Skip to content
This repository was archived by the owner on Mar 19, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions django/contrib/gis/db/models/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ def as_oracle(self, compiler, connection, **extra_context):
template = None if self.is_extent else '%(function)s(SDOAGGRTYPE(%(expressions)s,%(tolerance)s))'
return self.as_sql(compiler, connection, template=template, tolerance=tolerance, **extra_context)

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
for expr in c.get_source_expressions():
if not hasattr(expr.field, 'geom_type'):
raise ValueError('Geospatial aggregates only allowed on geometry fields.')
Expand Down
26 changes: 20 additions & 6 deletions django/contrib/postgres/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,20 @@ def __init__(self, *expressions, **extra):
weight = Value(weight)
self.weight = weight

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
resolved = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
if self.config:
if not hasattr(self.config, 'resolve_expression'):
resolved.config = Value(self.config).resolve_expression(query, allow_joins, reuse, summarize, for_save)
resolved.config = Value(self.config).resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
else:
resolved.config = self.config.resolve_expression(query, allow_joins, reuse, summarize, for_save)
resolved.config = self.config.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
return resolved

def as_sql(self, compiler, connection, function=None, template=None):
Expand Down Expand Up @@ -144,13 +151,20 @@ def __init__(self, value, output_field=None, *, config=None, invert=False, searc
self.search_type = search_type
super().__init__(value, output_field=output_field)

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
resolved = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
if self.config:
if not hasattr(self.config, 'resolve_expression'):
resolved.config = Value(self.config).resolve_expression(query, allow_joins, reuse, summarize, for_save)
resolved.config = Value(self.config).resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
else:
resolved.config = self.config.resolve_expression(query, allow_joins, reuse, summarize, for_save)
resolved.config = self.config.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
return resolved

def as_sql(self, compiler, connection):
Expand Down
9 changes: 7 additions & 2 deletions django/db/models/aggregates.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,15 @@ def set_source_expressions(self, exprs):
self.filter = self.filter and exprs.pop()
return super().set_source_expressions(exprs)

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
# Aggregates are not allowed in UPDATE queries, so ignore for_save
c = super().resolve_expression(query, allow_joins, reuse, summarize)
c.filter = c.filter and c.filter.resolve_expression(query, allow_joins, reuse, summarize)
c.filter = c.filter and c.filter.resolve_expression(
query, allow_joins, reuse, summarize, reuse_with_filtered_relation=reuse_with_filtered_relation
)
if not summarize:
# Call Aggregate.get_source_expressions() to avoid
# returning self.filter and including that in this loop.
Expand Down
81 changes: 61 additions & 20 deletions django/db/models/expressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ def contains_over_clause(self):
def contains_column_references(self):
return any(expr and expr.contains_column_references for expr in self.get_source_expressions())

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
"""
Provide the chance to do any preprocessing or validation before being
added to the query.
Expand All @@ -232,13 +235,16 @@ def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize
* reuse: a set of reusable joins for multijoins
* summarize: a terminal aggregate clause
* for_save: whether this expression about to be used in a save or update
* reuse_with_filtered_relation: if the reuse param should be used with FilteredRelations

Return: an Expression to be added to the query.
"""
c = self.copy()
c.is_summary = summarize
c.set_source_expressions([
expr.resolve_expression(query, allow_joins, reuse, summarize)
expr.resolve_expression(
query, allow_joins, reuse, summarize, reuse_with_filtered_relation=reuse_with_filtered_relation
)
if expr else None
for expr in c.get_source_expressions()
])
Expand Down Expand Up @@ -443,11 +449,14 @@ def as_sql(self, compiler, connection):
sql = connection.ops.combine_expression(self.connector, expressions)
return expression_wrapper % sql, expression_params

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = self.copy()
c.is_summary = summarize
c.lhs = c.lhs.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.rhs = c.rhs.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.lhs = c.lhs.resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
c.rhs = c.rhs.resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
return c


Expand Down Expand Up @@ -510,8 +519,10 @@ def __repr__(self):
return "{}({})".format(self.__class__.__name__, self.name)

def resolve_expression(self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, simple_col=False):
return query.resolve_ref(self.name, allow_joins, reuse, summarize, simple_col)
summarize=False, for_save=False, simple_col=False, reuse_with_filtered_relation=False):
return query.resolve_ref(
self.name, allow_joins, reuse, summarize, simple_col, reuse_with_filtered_relation
)

def asc(self, **kwargs):
return OrderBy(self, **kwargs)
Expand Down Expand Up @@ -548,7 +559,8 @@ def relabeled_clone(self, relabels):

class OuterRef(F):
def resolve_expression(self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, simple_col=False):
summarize=False, for_save=False, simple_col=False,
reuse_with_filtered_relation=False):
if isinstance(self.name, self.__class__):
return self.name
return ResolvedOuterRef(self.name)
Expand Down Expand Up @@ -596,11 +608,16 @@ def get_source_expressions(self):
def set_source_expressions(self, exprs):
self.source_expressions = exprs

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = self.copy()
c.is_summary = summarize
for pos, arg in enumerate(c.source_expressions):
c.source_expressions[pos] = arg.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.source_expressions[pos] = arg.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
return c

def as_sql(self, compiler, connection, function=None, template=None, arg_joiner=None, **extra_context):
Expand Down Expand Up @@ -666,8 +683,11 @@ def as_sql(self, compiler, connection):
return 'NULL', []
return '%s', [val]

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = super().resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
c.for_save = for_save
return c

Expand Down Expand Up @@ -801,7 +821,10 @@ def get_source_expressions(self):
def set_source_expressions(self, exprs):
self.source, = exprs

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
# The sub-expression `source` has already been resolved, as this is
# just a reference to the name of `source`.
return self
Expand Down Expand Up @@ -886,12 +909,19 @@ def get_source_fields(self):
# We're only interested in the fields of the result expressions.
return [self.result._output_field_or_none]

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = self.copy()
c.is_summary = summarize
if hasattr(c.condition, 'resolve_expression'):
c.condition = c.condition.resolve_expression(query, allow_joins, reuse, summarize, False)
c.result = c.result.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.condition = c.condition.resolve_expression(
query, allow_joins, reuse, summarize, False, reuse_with_filtered_relation
)
c.result = c.result.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
return c

def as_sql(self, compiler, connection, template=None, **extra_context):
Expand Down Expand Up @@ -950,12 +980,19 @@ def get_source_expressions(self):
def set_source_expressions(self, exprs):
*self.cases, self.default = exprs

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
c = self.copy()
c.is_summary = summarize
for pos, case in enumerate(c.cases):
c.cases[pos] = case.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.default = c.default.resolve_expression(query, allow_joins, reuse, summarize, for_save)
c.cases[pos] = case.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
c.default = c.default.resolve_expression(
query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation
)
return c

def copy(self):
Expand Down Expand Up @@ -1014,7 +1051,10 @@ def copy(self):
clone.queryset = clone.queryset.all()
return clone

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
clone = self.copy()
clone.is_summary = summarize
clone.queryset.query.bump_prefix(query)
Expand All @@ -1031,6 +1071,7 @@ def resolve(child):
resolved = child.resolve_expression(
query=query, allow_joins=allow_joins, reuse=reuse,
summarize=summarize, for_save=for_save,
reuse_with_filtered_relation=reuse_with_filtered_relation
)
# Add table alias to the parent query's aliases to prevent
# quoting.
Expand Down
14 changes: 10 additions & 4 deletions django/db/models/functions/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,11 @@ def as_sql(self, compiler, connection):
assert False, "Tried to Extract from an invalid type."
return sql, params

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
field = copy.lhs.output_field
if not isinstance(field, (DateField, DateTimeField, TimeField, DurationField)):
raise ValueError(
Expand Down Expand Up @@ -187,8 +190,11 @@ def as_sql(self, compiler, connection):
raise ValueError('Trunc only valid on DateField, TimeField, or DateTimeField.')
return sql, inner_params

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save, reuse_with_filtered_relation)
field = copy.lhs.output_field
# DateTimeField is a subclass of DateField so this works for both.
assert isinstance(field, (DateField, TimeField)), (
Expand Down
16 changes: 14 additions & 2 deletions django/db/models/query_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,10 @@ def __invert__(self):
obj.negate()
return obj

def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
def resolve_expression(
self, query=None, allow_joins=True, reuse=None,
summarize=False, for_save=False, reuse_with_filtered_relation=False
):
# We must promote any new joins to left outer joins so that when Q is
# used as an expression, rows aren't filtered due to joins.
clause, joins = query._add_q(self, reuse, allow_joins=allow_joins, split_subq=False)
Expand Down Expand Up @@ -332,5 +335,14 @@ def resolve_expression(self, *args, **kwargs):
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))
# Add other usable aliases in the query to the reuse set.
# Check for if it can be used is in Query.join
reusable_aliases = self.path
# TODO - determine which from the alias_map have actually been applied as joins,
# TODO limit to those and make sure enough are added in the setup_joins
reusable_aliases += list(query.alias_map.keys())
# import pdb
# pdb.set_trace()
# print("reusable_aliases", reusable_aliases)
where = query.build_filtered_relation_q(self.condition, reuse=set(reusable_aliases))
return compiler.compile(where)
8 changes: 8 additions & 0 deletions django/db/models/sql/datastructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,9 @@ def as_sql(self, compiler, connection):
join_conditions.append('(%s)' % extra_sql)
params.extend(extra_params)
if self.filtered_relation:
# print("compiling filtered relation", self.filtered_relation.relation_name)
extra_sql, extra_params = compiler.compile(self.filtered_relation)
# print("compiled filtered relation", extra_sql, extra_params)
if extra_sql:
join_conditions.append('(%s)' % extra_sql)
params.extend(extra_params)
Expand All @@ -98,7 +100,11 @@ def as_sql(self, compiler, connection):
"Join generated an empty ON clause. %s did not yield either "
"joining columns or extra restrictions." % declared_field.__class__
)
# TODO - on clause aliases specified here. too deep to change alias though
on_clause_sql = ' AND '.join(join_conditions)
# if self.table_name in ('filtered_relation_editor',) or self.table_alias in ('T4',):
# import pdb;
# pdb.set_trace()
alias_str = '' if self.table_alias == self.table_name else (' %s' % self.table_alias)
sql = '%s %s%s ON (%s)' % (self.join_type, qn(self.table_name), alias_str, on_clause_sql)
return sql, params
Expand All @@ -122,6 +128,8 @@ def equals(self, other, with_filtered_relation):
self.table_name == other.table_name and
self.parent_alias == other.parent_alias and
self.join_field == other.join_field and
# TODO - do any of these checks need changing?
# Like comparing the parent alias with a filtered relation
(not with_filtered_relation or self.filtered_relation == other.filtered_relation)
)

Expand Down
Loading