mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #18375 -- Removed dict-ordering dependency for F-expressions
F() expressions reuse joins like any lookup in a .filter() call - reuse multijoins generated in the same .filter() call else generate new joins. Also, lookups can now reuse joins generated by F(). This change is backwards incompatible, but it is required to prevent dict randomization from generating different queries depending on .filter() kwarg ordering. The new way is also more consistent in how joins are reused.
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
from django.core.exceptions import FieldError
|
||||
from django.db.models.constants import LOOKUP_SEP
|
||||
from django.db.models.fields import FieldDoesNotExist
|
||||
from django.db.models.sql.constants import REUSE_ALL
|
||||
|
||||
class SQLEvaluator(object):
|
||||
def __init__(self, expression, query, allow_joins=True):
|
||||
def __init__(self, expression, query, allow_joins=True, reuse=REUSE_ALL):
|
||||
self.expression = expression
|
||||
self.opts = query.get_meta()
|
||||
self.cols = []
|
||||
|
||||
self.contains_aggregate = False
|
||||
self.reuse = reuse
|
||||
self.expression.prepare(self, query, allow_joins)
|
||||
|
||||
def prepare(self):
|
||||
@@ -50,9 +52,10 @@ class SQLEvaluator(object):
|
||||
try:
|
||||
field, source, opts, join_list, last, _ = query.setup_joins(
|
||||
field_list, query.get_meta(),
|
||||
query.get_initial_alias(), False)
|
||||
query.get_initial_alias(), self.reuse)
|
||||
col, _, join_list = query.trim_joins(source, join_list, last, False)
|
||||
|
||||
if self.reuse is not None and self.reuse != REUSE_ALL:
|
||||
self.reuse.update(join_list)
|
||||
self.cols.append((node, (join_list[-1], col)))
|
||||
except FieldDoesNotExist:
|
||||
raise FieldError("Cannot resolve keyword %r into field. "
|
||||
|
||||
@@ -1096,7 +1096,7 @@ class Query(object):
|
||||
value = value()
|
||||
elif isinstance(value, ExpressionNode):
|
||||
# If value is a query expression, evaluate it
|
||||
value = SQLEvaluator(value, self)
|
||||
value = SQLEvaluator(value, self, reuse=can_reuse)
|
||||
having_clause = value.contains_aggregate
|
||||
|
||||
for alias, aggregate in self.aggregates.items():
|
||||
|
||||
Reference in New Issue
Block a user