mirror of https://github.com/django/django.git
Refs #27624 -- Made many attributes of Query immutable.
This commit is contained in:
parent
5db465d5a6
commit
af121b08e8
|
@ -6,7 +6,6 @@ themselves do not have to (and could be backed by things other than SQL
|
|||
databases). The abstraction barrier only works one way: this module has to know
|
||||
all about the internals of models in order to get the information it needs.
|
||||
"""
|
||||
import copy
|
||||
from collections import Counter, Iterator, Mapping, OrderedDict
|
||||
from itertools import chain, count, product
|
||||
from string import ascii_uppercase
|
||||
|
@ -145,21 +144,21 @@ class Query:
|
|||
# The select is used for cases where we want to set up the select
|
||||
# clause to contain other than default fields (values(), subqueries...)
|
||||
# Note that annotations go to annotations dictionary.
|
||||
self.select = []
|
||||
self.tables = [] # Aliases in the order they are created.
|
||||
self.select = ()
|
||||
self.tables = () # Aliases in the order they are created.
|
||||
self.where = where()
|
||||
self.where_class = where
|
||||
# The group_by attribute can have one of the following forms:
|
||||
# - None: no group by at all in the query
|
||||
# - A list of expressions: group by (at least) those expressions.
|
||||
# - A tuple of expressions: group by (at least) those expressions.
|
||||
# String refs are also allowed for now.
|
||||
# - True: group by all select fields of the model
|
||||
# See compiler.get_group_by() for details.
|
||||
self.group_by = None
|
||||
self.order_by = []
|
||||
self.order_by = ()
|
||||
self.low_mark, self.high_mark = 0, None # Used for offset/limit
|
||||
self.distinct = False
|
||||
self.distinct_fields = []
|
||||
self.distinct_fields = ()
|
||||
self.select_for_update = False
|
||||
self.select_for_update_nowait = False
|
||||
self.select_for_update_skip_locked = False
|
||||
|
@ -170,7 +169,7 @@ class Query:
|
|||
|
||||
# Holds the selects defined by a call to values() or values_list()
|
||||
# excluding annotation_select and extra_select.
|
||||
self.values_select = []
|
||||
self.values_select = ()
|
||||
|
||||
# SQL annotation-related attributes
|
||||
# The _annotations will be an OrderedDict when used. Due to the cost
|
||||
|
@ -199,7 +198,7 @@ class Query:
|
|||
# A tuple that is a set of model field names and either True, if these
|
||||
# are the fields to defer, or False if these are the only fields to
|
||||
# load.
|
||||
self.deferred_loading = (set(), True)
|
||||
self.deferred_loading = (frozenset(), True)
|
||||
|
||||
self.context = {}
|
||||
|
||||
|
@ -271,25 +270,20 @@ class Query:
|
|||
obj.default_cols = self.default_cols
|
||||
obj.default_ordering = self.default_ordering
|
||||
obj.standard_ordering = self.standard_ordering
|
||||
obj.select = self.select[:]
|
||||
obj.tables = self.tables[:]
|
||||
obj.select = self.select
|
||||
obj.tables = self.tables
|
||||
obj.where = self.where.clone()
|
||||
obj.where_class = self.where_class
|
||||
if self.group_by is None:
|
||||
obj.group_by = None
|
||||
elif self.group_by is True:
|
||||
obj.group_by = True
|
||||
else:
|
||||
obj.group_by = self.group_by[:]
|
||||
obj.order_by = self.order_by[:]
|
||||
obj.group_by = self.group_by
|
||||
obj.order_by = self.order_by
|
||||
obj.low_mark, obj.high_mark = self.low_mark, self.high_mark
|
||||
obj.distinct = self.distinct
|
||||
obj.distinct_fields = self.distinct_fields[:]
|
||||
obj.distinct_fields = self.distinct_fields
|
||||
obj.select_for_update = self.select_for_update
|
||||
obj.select_for_update_nowait = self.select_for_update_nowait
|
||||
obj.select_for_update_skip_locked = self.select_for_update_skip_locked
|
||||
obj.select_related = self.select_related
|
||||
obj.values_select = self.values_select[:]
|
||||
obj.values_select = self.values_select
|
||||
obj._annotations = self._annotations.copy() if self._annotations is not None else None
|
||||
if self.annotation_select_mask is None:
|
||||
obj.annotation_select_mask = None
|
||||
|
@ -316,7 +310,7 @@ class Query:
|
|||
obj._extra_select_cache = self._extra_select_cache.copy()
|
||||
obj.extra_tables = self.extra_tables
|
||||
obj.extra_order_by = self.extra_order_by
|
||||
obj.deferred_loading = copy.copy(self.deferred_loading[0]), self.deferred_loading[1]
|
||||
obj.deferred_loading = self.deferred_loading
|
||||
if self.filter_is_sticky and self.used_aliases:
|
||||
obj.used_aliases = self.used_aliases.copy()
|
||||
else:
|
||||
|
@ -412,7 +406,7 @@ class Query:
|
|||
# done in a subquery so that we are aggregating on the limit and/or
|
||||
# distinct results instead of applying the distinct and limit after the
|
||||
# aggregation.
|
||||
if (isinstance(self.group_by, list) or has_limit or has_existing_annotations or
|
||||
if (isinstance(self.group_by, tuple) or has_limit or has_existing_annotations or
|
||||
self.distinct):
|
||||
from django.db.models.sql.subqueries import AggregateQuery
|
||||
outer_query = AggregateQuery(self.model)
|
||||
|
@ -431,7 +425,7 @@ class Query:
|
|||
# clearing the select clause can alter results if distinct is
|
||||
# used.
|
||||
if inner_query.default_cols and has_existing_annotations:
|
||||
inner_query.group_by = [self.model._meta.pk.get_col(inner_query.get_initial_alias())]
|
||||
inner_query.group_by = (self.model._meta.pk.get_col(inner_query.get_initial_alias()),)
|
||||
inner_query.default_cols = False
|
||||
|
||||
relabels = {t: 'subquery' for t in inner_query.tables}
|
||||
|
@ -446,11 +440,11 @@ class Query:
|
|||
del inner_query.annotations[alias]
|
||||
# Make sure the annotation_select wont use cached results.
|
||||
inner_query.set_annotation_mask(inner_query.annotation_select_mask)
|
||||
if inner_query.select == [] and not inner_query.default_cols and not inner_query.annotation_select_mask:
|
||||
if inner_query.select == () and not inner_query.default_cols and not inner_query.annotation_select_mask:
|
||||
# In case of Model.objects[0:3].count(), there would be no
|
||||
# field selected in the inner query, yet we must use a subquery.
|
||||
# So, make sure at least one field is selected.
|
||||
inner_query.select = [self.model._meta.pk.get_col(inner_query.get_initial_alias())]
|
||||
inner_query.select = (self.model._meta.pk.get_col(inner_query.get_initial_alias()),)
|
||||
try:
|
||||
outer_query.add_subquery(inner_query, using)
|
||||
except EmptyResultSet:
|
||||
|
@ -460,7 +454,7 @@ class Query:
|
|||
}
|
||||
else:
|
||||
outer_query = self
|
||||
self.select = []
|
||||
self.select = ()
|
||||
self.default_cols = False
|
||||
self._extra = {}
|
||||
|
||||
|
@ -582,9 +576,10 @@ class Query:
|
|||
self.where.add(w, connector)
|
||||
|
||||
# Selection columns and extra extensions are those provided by 'rhs'.
|
||||
self.select = []
|
||||
for col in rhs.select:
|
||||
self.add_select(col.relabeled_clone(change_map))
|
||||
if rhs.select:
|
||||
self.set_select([col.relabeled_clone(change_map) for col in rhs.select])
|
||||
else:
|
||||
self.select = ()
|
||||
|
||||
if connector == OR:
|
||||
# It would be nice to be able to handle this, but the queries don't
|
||||
|
@ -604,7 +599,7 @@ class Query:
|
|||
|
||||
# Ordering uses the 'rhs' ordering, unless it has none, in which case
|
||||
# the current ordering is used.
|
||||
self.order_by = rhs.order_by[:] if rhs.order_by else self.order_by
|
||||
self.order_by = rhs.order_by if rhs.order_by else self.order_by
|
||||
self.extra_order_by = rhs.extra_order_by or self.extra_order_by
|
||||
|
||||
def deferred_to_data(self, target, callback):
|
||||
|
@ -716,7 +711,7 @@ class Query:
|
|||
alias = table_name
|
||||
self.table_map[alias] = [alias]
|
||||
self.alias_refcount[alias] = 1
|
||||
self.tables.append(alias)
|
||||
self.tables += (alias,)
|
||||
return alias, True
|
||||
|
||||
def ref_alias(self, alias):
|
||||
|
@ -800,9 +795,9 @@ class Query:
|
|||
# 1. Update references in "select" (normal columns plus aliases),
|
||||
# "group by" and "where".
|
||||
self.where.relabel_aliases(change_map)
|
||||
if isinstance(self.group_by, list):
|
||||
self.group_by = [col.relabeled_clone(change_map) for col in self.group_by]
|
||||
self.select = [col.relabeled_clone(change_map) for col in self.select]
|
||||
if isinstance(self.group_by, tuple):
|
||||
self.group_by = tuple([col.relabeled_clone(change_map) for col in self.group_by])
|
||||
self.select = tuple([col.relabeled_clone(change_map) for col in self.select])
|
||||
if self._annotations:
|
||||
self._annotations = OrderedDict(
|
||||
(key, col.relabeled_clone(change_map)) for key, col in self._annotations.items())
|
||||
|
@ -866,10 +861,12 @@ class Query:
|
|||
self.subq_aliases = self.subq_aliases.union([self.alias_prefix])
|
||||
outer_query.subq_aliases = outer_query.subq_aliases.union(self.subq_aliases)
|
||||
change_map = OrderedDict()
|
||||
for pos, alias in enumerate(self.tables):
|
||||
tables = list(self.tables)
|
||||
for pos, alias in enumerate(tables):
|
||||
new_alias = '%s%d' % (self.alias_prefix, pos)
|
||||
change_map[alias] = new_alias
|
||||
self.tables[pos] = new_alias
|
||||
tables[pos] = new_alias
|
||||
self.tables = tuple(tables)
|
||||
self.change_aliases(change_map)
|
||||
|
||||
def get_initial_alias(self):
|
||||
|
@ -1577,7 +1574,7 @@ class Query:
|
|||
|
||||
def clear_select_clause(self):
|
||||
"""Remove all fields from SELECT clause."""
|
||||
self.select = []
|
||||
self.select = ()
|
||||
self.default_cols = False
|
||||
self.select_related = False
|
||||
self.set_extra_mask(())
|
||||
|
@ -1589,16 +1586,12 @@ class Query:
|
|||
Some queryset types completely replace any existing list of select
|
||||
columns.
|
||||
"""
|
||||
self.select = []
|
||||
self.values_select = []
|
||||
|
||||
def add_select(self, col):
|
||||
self.default_cols = False
|
||||
self.select.append(col)
|
||||
self.select = ()
|
||||
self.values_select = ()
|
||||
|
||||
def set_select(self, cols):
|
||||
self.default_cols = False
|
||||
self.select = cols
|
||||
self.select = tuple(cols)
|
||||
|
||||
def add_distinct_fields(self, *field_names):
|
||||
"""
|
||||
|
@ -1616,6 +1609,7 @@ class Query:
|
|||
opts = self.get_meta()
|
||||
|
||||
try:
|
||||
cols = []
|
||||
for name in field_names:
|
||||
# Join promotion note - we must not remove any rows here, so
|
||||
# if there is no existing joins, use outer join.
|
||||
|
@ -1623,7 +1617,9 @@ class Query:
|
|||
name.split(LOOKUP_SEP), opts, alias, allow_many=allow_m2m)
|
||||
targets, final_alias, joins = self.trim_joins(targets, joins, path)
|
||||
for target in targets:
|
||||
self.add_select(target.get_col(final_alias))
|
||||
cols.append(target.get_col(final_alias))
|
||||
if cols:
|
||||
self.set_select(cols)
|
||||
except MultiJoin:
|
||||
raise FieldError("Invalid field name: '%s'" % name)
|
||||
except FieldError:
|
||||
|
@ -1657,7 +1653,7 @@ class Query:
|
|||
if errors:
|
||||
raise FieldError('Invalid order_by arguments: %s' % errors)
|
||||
if ordering:
|
||||
self.order_by.extend(ordering)
|
||||
self.order_by += ordering
|
||||
else:
|
||||
self.default_ordering = False
|
||||
|
||||
|
@ -1666,7 +1662,7 @@ class Query:
|
|||
Remove any ordering settings. If 'force_empty' is True, there will be
|
||||
no ordering in the resulting query (not even the model's default).
|
||||
"""
|
||||
self.order_by = []
|
||||
self.order_by = ()
|
||||
self.extra_order_by = ()
|
||||
if force_empty:
|
||||
self.default_ordering = False
|
||||
|
@ -1680,15 +1676,12 @@ class Query:
|
|||
primary key, and the query would be equivalent, the optimization
|
||||
will be made automatically.
|
||||
"""
|
||||
self.group_by = []
|
||||
|
||||
for col in self.select:
|
||||
self.group_by.append(col)
|
||||
|
||||
group_by = list(self.select)
|
||||
if self.annotation_select:
|
||||
for alias, annotation in self.annotation_select.items():
|
||||
for col in annotation.get_group_by_cols():
|
||||
self.group_by.append(col)
|
||||
group_by.append(col)
|
||||
self.group_by = tuple(group_by)
|
||||
|
||||
def add_select_related(self, fields):
|
||||
"""
|
||||
|
@ -1741,7 +1734,7 @@ class Query:
|
|||
|
||||
def clear_deferred_loading(self):
|
||||
"""Remove any fields from the deferred loading set."""
|
||||
self.deferred_loading = (set(), True)
|
||||
self.deferred_loading = (frozenset(), True)
|
||||
|
||||
def add_deferred_loading(self, field_names):
|
||||
"""
|
||||
|
@ -1785,7 +1778,7 @@ class Query:
|
|||
self.deferred_loading = field_names.difference(existing), False
|
||||
else:
|
||||
# Replace any existing "immediate load" field names.
|
||||
self.deferred_loading = field_names, False
|
||||
self.deferred_loading = frozenset(field_names), False
|
||||
|
||||
def get_loaded_field_names(self):
|
||||
"""
|
||||
|
@ -1865,7 +1858,7 @@ class Query:
|
|||
else:
|
||||
field_names = [f.attname for f in self.model._meta.concrete_fields]
|
||||
|
||||
self.values_select = field_names
|
||||
self.values_select = tuple(field_names)
|
||||
self.add_fields(field_names, True)
|
||||
|
||||
@property
|
||||
|
|
|
@ -19,7 +19,7 @@ class DeleteQuery(Query):
|
|||
compiler = 'SQLDeleteCompiler'
|
||||
|
||||
def do_query(self, table, where, using):
|
||||
self.tables = [table]
|
||||
self.tables = (table,)
|
||||
self.where = where
|
||||
cursor = self.get_compiler(using).execute_sql(CURSOR)
|
||||
return cursor.rowcount if cursor else 0
|
||||
|
@ -52,8 +52,7 @@ class DeleteQuery(Query):
|
|||
innerq.get_initial_alias()
|
||||
# The same for our new query.
|
||||
self.get_initial_alias()
|
||||
innerq_used_tables = [t for t in innerq.tables
|
||||
if innerq.alias_refcount[t]]
|
||||
innerq_used_tables = tuple([t for t in innerq.tables if innerq.alias_refcount[t]])
|
||||
if not innerq_used_tables or innerq_used_tables == self.tables:
|
||||
# There is only the base table in use in the query.
|
||||
self.where = innerq.where
|
||||
|
|
Loading…
Reference in New Issue