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]
if isinstance(k, slice):
qs = self._clone()
qs = self._chain()
if k.start is not None:
start = int(k.start)
else:
@ -273,7 +273,7 @@ class QuerySet:
qs.query.set_limits(start, stop)
return list(qs)[::k.step] if k.step else qs
qs = self._clone()
qs = self._chain()
qs.query.set_limits(k, k + 1)
qs._fetch_all()
return qs._result_cache[0]
@ -284,7 +284,7 @@ class QuerySet:
return other
if isinstance(self, EmptyQuerySet):
return self
combined = self._clone()
combined = self._chain()
combined._merge_known_related_objects(other)
combined.query.combine(other.query, sql.AND)
return combined
@ -295,7 +295,7 @@ class QuerySet:
return other
if isinstance(other, EmptyQuerySet):
return self
combined = self._clone()
combined = self._chain()
combined._merge_known_related_objects(other)
combined.query.combine(other.query, sql.OR)
return combined
@ -535,7 +535,7 @@ class QuerySet:
"field_name parameter or 'get_latest_by' in the model"
assert self.query.can_filter(), \
"Cannot change a query once a slice has been taken."
obj = self._clone()
obj = self._chain()
obj.query.set_limits(high=1)
obj.query.clear_ordering(force_empty=True)
obj.query.add_ordering('%s%s' % (direction, order_by))
@ -582,7 +582,7 @@ class QuerySet:
else:
qs = self.filter(**{filter_key: id_list}).order_by()
else:
qs = self._clone()
qs = self._chain()
return {getattr(obj, field_name): obj for obj in qs}
def delete(self):
@ -593,7 +593,7 @@ class QuerySet:
if self._fields is not None:
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,
# 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)
def _values(self, *fields, **expressions):
clone = self._clone()
clone = self._chain()
if expressions:
clone = clone.annotate(**expressions)
clone._fields = fields
@ -748,7 +748,7 @@ class QuerySet:
def none(self):
"""Return an empty QuerySet."""
clone = self._clone()
clone = self._chain()
clone.query.set_empty()
return clone
@ -761,7 +761,7 @@ class QuerySet:
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.
"""
return self._clone()
return self._chain()
def filter(self, *args, **kwargs):
"""
@ -782,7 +782,7 @@ class QuerySet:
assert self.query.can_filter(), \
"Cannot filter a query once a slice has been taken."
clone = self._clone()
clone = self._chain()
if negate:
clone.query.add_q(~Q(*args, **kwargs))
else:
@ -800,7 +800,7 @@ class QuerySet:
and usually it will be more natural to use other methods.
"""
if isinstance(filter_obj, Q):
clone = self._clone()
clone = self._chain()
clone.query.add_q(filter_obj)
return clone
else:
@ -808,7 +808,7 @@ class QuerySet:
def _combinator_query(self, combinator, *other_qs, all=False):
# 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
clone.query.clear_ordering(True)
clone.query.clear_limits()
@ -846,7 +846,7 @@ class QuerySet:
"""
if nowait and skip_locked:
raise ValueError('The nowait option cannot be used with skip_locked.')
obj = self._clone()
obj = self._chain()
obj._for_write = True
obj.query.select_for_update = True
obj.query.select_for_update_nowait = nowait
@ -867,7 +867,7 @@ class QuerySet:
if self._fields is not None:
raise TypeError("Cannot call select_related() after .values() or .values_list()")
obj = self._clone()
obj = self._chain()
if fields == (None,):
obj.query.select_related = False
elif fields:
@ -885,7 +885,7 @@ class QuerySet:
When prefetch_related() is called more than once, append to the list of
prefetch lookups. If prefetch_related(None) is called, clear the list.
"""
clone = self._clone()
clone = self._chain()
if lookups == (None,):
clone._prefetch_related_lookups = ()
else:
@ -911,7 +911,7 @@ class QuerySet:
annotations[arg.default_alias] = arg
annotations.update(kwargs)
clone = self._clone()
clone = self._chain()
names = self._fields
if names is None:
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."""
assert self.query.can_filter(), \
"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.add_ordering(*field_names)
return obj
@ -947,7 +947,7 @@ class QuerySet:
"""
assert self.query.can_filter(), \
"Cannot create distinct fields once a slice has been taken."
obj = self._clone()
obj = self._chain()
obj.query.add_distinct_fields(*field_names)
return obj
@ -956,7 +956,7 @@ class QuerySet:
"""Add extra SQL fragments to the query."""
assert self.query.can_filter(), \
"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)
return clone
@ -964,7 +964,7 @@ class QuerySet:
"""Reverse the ordering of the QuerySet."""
if not self.query.can_filter():
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
return clone
@ -977,7 +977,7 @@ class QuerySet:
"""
if self._fields is not None:
raise TypeError("Cannot call defer() after .values() or .values_list()")
clone = self._clone()
clone = self._chain()
if fields == (None,):
clone.query.clear_deferred_loading()
else:
@ -996,13 +996,13 @@ class QuerySet:
# 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.
raise TypeError("Cannot pass None as an argument to only().")
clone = self._clone()
clone = self._chain()
clone.query.add_immediate_loading(fields)
return clone
def using(self, alias):
"""Select which database this QuerySet should execute against."""
clone = self._clone()
clone = self._chain()
clone._db = alias
return clone
@ -1070,19 +1070,31 @@ class QuerySet:
self._insert(item, fields=fields, using=self.db)
return inserted_ids
def _clone(self, **kwargs):
query = self.query.clone()
if self._sticky_filter:
query.filter_is_sticky = True
clone = self.__class__(model=self.model, query=query, using=self._db, hints=self._hints)
clone._for_write = self._for_write
clone._prefetch_related_lookups = self._prefetch_related_lookups
clone._known_related_objects = self._known_related_objects
clone._iterable_class = self._iterable_class
clone._fields = self._fields
def _chain(self, **kwargs):
"""
Return a copy of the current QuerySet that's ready for another
operation.
"""
obj = self._clone()
if obj._sticky_filter:
obj.query.filter_is_sticky = True
obj._sticky_filter = False
obj.__dict__.update(kwargs)
return obj
clone.__dict__.update(kwargs)
return clone
def _clone(self):
"""
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):
if self._result_cache is None:
@ -1304,7 +1316,7 @@ class Prefetch:
obj_dict = self.__dict__.copy()
if self.queryset is not None:
# Prevent the QuerySet from being evaluated
obj_dict['queryset'] = self.queryset._clone(
obj_dict['queryset'] = self.queryset._chain(
_result_cache=[],
_prefetch_done=True,
)