Refs #20880 -- Removed non-cloning logic from QuerySet._clone().

This commit is contained in:
Anssi Kääriäinen 2013-08-08 12:56:40 +03:00 committed by Tim Graham
parent 0baea920c8
commit 66933a6619
1 changed files with 49 additions and 37 deletions

View File

@ -261,7 +261,7 @@ class QuerySet:
return self._result_cache[k] return self._result_cache[k]
if isinstance(k, slice): if isinstance(k, slice):
qs = self._clone() qs = self._chain()
if k.start is not None: if k.start is not None:
start = int(k.start) start = int(k.start)
else: else:
@ -273,7 +273,7 @@ class QuerySet:
qs.query.set_limits(start, stop) qs.query.set_limits(start, stop)
return list(qs)[::k.step] if k.step else qs return list(qs)[::k.step] if k.step else qs
qs = self._clone() qs = self._chain()
qs.query.set_limits(k, k + 1) qs.query.set_limits(k, k + 1)
qs._fetch_all() qs._fetch_all()
return qs._result_cache[0] return qs._result_cache[0]
@ -284,7 +284,7 @@ class QuerySet:
return other return other
if isinstance(self, EmptyQuerySet): if isinstance(self, EmptyQuerySet):
return self return self
combined = self._clone() combined = self._chain()
combined._merge_known_related_objects(other) combined._merge_known_related_objects(other)
combined.query.combine(other.query, sql.AND) combined.query.combine(other.query, sql.AND)
return combined return combined
@ -295,7 +295,7 @@ class QuerySet:
return other return other
if isinstance(other, EmptyQuerySet): if isinstance(other, EmptyQuerySet):
return self return self
combined = self._clone() combined = self._chain()
combined._merge_known_related_objects(other) combined._merge_known_related_objects(other)
combined.query.combine(other.query, sql.OR) combined.query.combine(other.query, sql.OR)
return combined return combined
@ -535,7 +535,7 @@ class QuerySet:
"field_name parameter or 'get_latest_by' in the model" "field_name parameter or 'get_latest_by' in the model"
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot change a query once a slice has been taken." "Cannot change a query once a slice has been taken."
obj = self._clone() obj = self._chain()
obj.query.set_limits(high=1) obj.query.set_limits(high=1)
obj.query.clear_ordering(force_empty=True) obj.query.clear_ordering(force_empty=True)
obj.query.add_ordering('%s%s' % (direction, order_by)) obj.query.add_ordering('%s%s' % (direction, order_by))
@ -582,7 +582,7 @@ class QuerySet:
else: else:
qs = self.filter(**{filter_key: id_list}).order_by() qs = self.filter(**{filter_key: id_list}).order_by()
else: else:
qs = self._clone() qs = self._chain()
return {getattr(obj, field_name): obj for obj in qs} return {getattr(obj, field_name): obj for obj in qs}
def delete(self): def delete(self):
@ -593,7 +593,7 @@ class QuerySet:
if self._fields is not None: if self._fields is not None:
raise TypeError("Cannot call delete() after .values() or .values_list()") raise TypeError("Cannot call delete() after .values() or .values_list()")
del_query = self._clone() del_query = self._chain()
# The delete is actually 2 queries - one to find related objects, # The delete is actually 2 queries - one to find related objects,
# and one to delete. Make sure that the discovery of related # and one to delete. Make sure that the discovery of related
@ -678,7 +678,7 @@ class QuerySet:
return RawQuerySet(raw_query, model=self.model, params=params, translations=translations, using=using) return RawQuerySet(raw_query, model=self.model, params=params, translations=translations, using=using)
def _values(self, *fields, **expressions): def _values(self, *fields, **expressions):
clone = self._clone() clone = self._chain()
if expressions: if expressions:
clone = clone.annotate(**expressions) clone = clone.annotate(**expressions)
clone._fields = fields clone._fields = fields
@ -748,7 +748,7 @@ class QuerySet:
def none(self): def none(self):
"""Return an empty QuerySet.""" """Return an empty QuerySet."""
clone = self._clone() clone = self._chain()
clone.query.set_empty() clone.query.set_empty()
return clone return clone
@ -761,7 +761,7 @@ class QuerySet:
Return a new QuerySet that is a copy of the current one. This allows a Return a new QuerySet that is a copy of the current one. This allows a
QuerySet to proxy for a model manager in some cases. QuerySet to proxy for a model manager in some cases.
""" """
return self._clone() return self._chain()
def filter(self, *args, **kwargs): def filter(self, *args, **kwargs):
""" """
@ -782,7 +782,7 @@ class QuerySet:
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot filter a query once a slice has been taken." "Cannot filter a query once a slice has been taken."
clone = self._clone() clone = self._chain()
if negate: if negate:
clone.query.add_q(~Q(*args, **kwargs)) clone.query.add_q(~Q(*args, **kwargs))
else: else:
@ -800,7 +800,7 @@ class QuerySet:
and usually it will be more natural to use other methods. and usually it will be more natural to use other methods.
""" """
if isinstance(filter_obj, Q): if isinstance(filter_obj, Q):
clone = self._clone() clone = self._chain()
clone.query.add_q(filter_obj) clone.query.add_q(filter_obj)
return clone return clone
else: else:
@ -808,7 +808,7 @@ class QuerySet:
def _combinator_query(self, combinator, *other_qs, all=False): def _combinator_query(self, combinator, *other_qs, all=False):
# Clone the query to inherit the select list and everything # Clone the query to inherit the select list and everything
clone = self._clone() clone = self._chain()
# Clear limits and ordering so they can be reapplied # Clear limits and ordering so they can be reapplied
clone.query.clear_ordering(True) clone.query.clear_ordering(True)
clone.query.clear_limits() clone.query.clear_limits()
@ -846,7 +846,7 @@ class QuerySet:
""" """
if nowait and skip_locked: if nowait and skip_locked:
raise ValueError('The nowait option cannot be used with skip_locked.') raise ValueError('The nowait option cannot be used with skip_locked.')
obj = self._clone() obj = self._chain()
obj._for_write = True obj._for_write = True
obj.query.select_for_update = True obj.query.select_for_update = True
obj.query.select_for_update_nowait = nowait obj.query.select_for_update_nowait = nowait
@ -867,7 +867,7 @@ class QuerySet:
if self._fields is not None: if self._fields is not None:
raise TypeError("Cannot call select_related() after .values() or .values_list()") raise TypeError("Cannot call select_related() after .values() or .values_list()")
obj = self._clone() obj = self._chain()
if fields == (None,): if fields == (None,):
obj.query.select_related = False obj.query.select_related = False
elif fields: elif fields:
@ -885,7 +885,7 @@ class QuerySet:
When prefetch_related() is called more than once, append to the list of When prefetch_related() is called more than once, append to the list of
prefetch lookups. If prefetch_related(None) is called, clear the list. prefetch lookups. If prefetch_related(None) is called, clear the list.
""" """
clone = self._clone() clone = self._chain()
if lookups == (None,): if lookups == (None,):
clone._prefetch_related_lookups = () clone._prefetch_related_lookups = ()
else: else:
@ -911,7 +911,7 @@ class QuerySet:
annotations[arg.default_alias] = arg annotations[arg.default_alias] = arg
annotations.update(kwargs) annotations.update(kwargs)
clone = self._clone() clone = self._chain()
names = self._fields names = self._fields
if names is None: if names is None:
names = {f.name for f in self.model._meta.get_fields()} names = {f.name for f in self.model._meta.get_fields()}
@ -936,7 +936,7 @@ class QuerySet:
"""Return a new QuerySet instance with the ordering changed.""" """Return a new QuerySet instance with the ordering changed."""
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot reorder a query once a slice has been taken." "Cannot reorder a query once a slice has been taken."
obj = self._clone() obj = self._chain()
obj.query.clear_ordering(force_empty=False) obj.query.clear_ordering(force_empty=False)
obj.query.add_ordering(*field_names) obj.query.add_ordering(*field_names)
return obj return obj
@ -947,7 +947,7 @@ class QuerySet:
""" """
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot create distinct fields once a slice has been taken." "Cannot create distinct fields once a slice has been taken."
obj = self._clone() obj = self._chain()
obj.query.add_distinct_fields(*field_names) obj.query.add_distinct_fields(*field_names)
return obj return obj
@ -956,7 +956,7 @@ class QuerySet:
"""Add extra SQL fragments to the query.""" """Add extra SQL fragments to the query."""
assert self.query.can_filter(), \ assert self.query.can_filter(), \
"Cannot change a query once a slice has been taken" "Cannot change a query once a slice has been taken"
clone = self._clone() clone = self._chain()
clone.query.add_extra(select, select_params, where, params, tables, order_by) clone.query.add_extra(select, select_params, where, params, tables, order_by)
return clone return clone
@ -964,7 +964,7 @@ class QuerySet:
"""Reverse the ordering of the QuerySet.""" """Reverse the ordering of the QuerySet."""
if not self.query.can_filter(): if not self.query.can_filter():
raise TypeError('Cannot reverse a query once a slice has been taken.') raise TypeError('Cannot reverse a query once a slice has been taken.')
clone = self._clone() clone = self._chain()
clone.query.standard_ordering = not clone.query.standard_ordering clone.query.standard_ordering = not clone.query.standard_ordering
return clone return clone
@ -977,7 +977,7 @@ class QuerySet:
""" """
if self._fields is not None: if self._fields is not None:
raise TypeError("Cannot call defer() after .values() or .values_list()") raise TypeError("Cannot call defer() after .values() or .values_list()")
clone = self._clone() clone = self._chain()
if fields == (None,): if fields == (None,):
clone.query.clear_deferred_loading() clone.query.clear_deferred_loading()
else: else:
@ -996,13 +996,13 @@ class QuerySet:
# Can only pass None to defer(), not only(), as the rest option. # Can only pass None to defer(), not only(), as the rest option.
# That won't stop people trying to do this, so let's be explicit. # That won't stop people trying to do this, so let's be explicit.
raise TypeError("Cannot pass None as an argument to only().") raise TypeError("Cannot pass None as an argument to only().")
clone = self._clone() clone = self._chain()
clone.query.add_immediate_loading(fields) clone.query.add_immediate_loading(fields)
return clone return clone
def using(self, alias): def using(self, alias):
"""Select which database this QuerySet should execute against.""" """Select which database this QuerySet should execute against."""
clone = self._clone() clone = self._chain()
clone._db = alias clone._db = alias
return clone return clone
@ -1070,19 +1070,31 @@ class QuerySet:
self._insert(item, fields=fields, using=self.db) self._insert(item, fields=fields, using=self.db)
return inserted_ids return inserted_ids
def _clone(self, **kwargs): def _chain(self, **kwargs):
query = self.query.clone() """
if self._sticky_filter: Return a copy of the current QuerySet that's ready for another
query.filter_is_sticky = True operation.
clone = self.__class__(model=self.model, query=query, using=self._db, hints=self._hints) """
clone._for_write = self._for_write obj = self._clone()
clone._prefetch_related_lookups = self._prefetch_related_lookups if obj._sticky_filter:
clone._known_related_objects = self._known_related_objects obj.query.filter_is_sticky = True
clone._iterable_class = self._iterable_class obj._sticky_filter = False
clone._fields = self._fields obj.__dict__.update(kwargs)
return obj
clone.__dict__.update(kwargs) def _clone(self):
return clone """
Return a copy of the current QuerySet. A lightweight alternative
to deepcopy().
"""
c = self.__class__(model=self.model, query=self.query.clone(), using=self._db, hints=self._hints)
c._sticky_filter = self._sticky_filter
c._for_write = self._for_write
c._prefetch_related_lookups = self._prefetch_related_lookups[:]
c._known_related_objects = self._known_related_objects
c._iterable_class = self._iterable_class
c._fields = self._fields
return c
def _fetch_all(self): def _fetch_all(self):
if self._result_cache is None: if self._result_cache is None:
@ -1304,7 +1316,7 @@ class Prefetch:
obj_dict = self.__dict__.copy() obj_dict = self.__dict__.copy()
if self.queryset is not None: if self.queryset is not None:
# Prevent the QuerySet from being evaluated # Prevent the QuerySet from being evaluated
obj_dict['queryset'] = self.queryset._clone( obj_dict['queryset'] = self.queryset._chain(
_result_cache=[], _result_cache=[],
_prefetch_done=True, _prefetch_done=True,
) )