1
0
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:
Anssi Kääriäinen
2012-11-22 20:27:28 +02:00
parent 7b9a1fb964
commit 90b86291d0
4 changed files with 52 additions and 4 deletions

View File

@@ -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. "

View File

@@ -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():