diff --git a/django/db/models/sql/aggregates.py b/django/db/models/sql/aggregates.py
index 207bc0c..7328660 100644
a
|
b
|
|
1 | 1 | """ |
2 | 2 | Classes to represent the default SQL aggregate functions |
3 | 3 | """ |
| 4 | import copy |
4 | 5 | |
5 | 6 | class AggregateField(object): |
6 | 7 | """An internal field mockup used to identify aggregates in the |
… |
… |
class Aggregate(object):
|
69 | 70 | |
70 | 71 | self.field = tmp |
71 | 72 | |
| 73 | def clone(self): |
| 74 | # Different aggregates have different init methods, so use copy here |
| 75 | # deepcopy is not needed, as self.col is only changing variable. |
| 76 | return copy.copy(self) |
| 77 | |
72 | 78 | def relabel_aliases(self, change_map): |
73 | 79 | if isinstance(self.col, (list, tuple)): |
74 | 80 | self.col = (change_map.get(self.col[0], self.col[0]), self.col[1]) |
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index ed2bc06..b669673 100644
a
|
b
|
class Query(object):
|
256 | 256 | obj.dupe_avoidance = self.dupe_avoidance.copy() |
257 | 257 | obj.select = self.select[:] |
258 | 258 | obj.tables = self.tables[:] |
259 | | obj.where = copy.deepcopy(self.where, memo=memo) |
| 259 | obj.where = self.where.clone() |
260 | 260 | obj.where_class = self.where_class |
261 | 261 | if self.group_by is None: |
262 | 262 | obj.group_by = None |
263 | 263 | else: |
264 | 264 | obj.group_by = self.group_by[:] |
265 | | obj.having = copy.deepcopy(self.having, memo=memo) |
| 265 | obj.having = self.having.clone() |
266 | 266 | obj.order_by = self.order_by[:] |
267 | 267 | obj.low_mark, obj.high_mark = self.low_mark, self.high_mark |
268 | 268 | obj.distinct = self.distinct |
… |
… |
class Query(object):
|
271 | 271 | obj.select_for_update_nowait = self.select_for_update_nowait |
272 | 272 | obj.select_related = self.select_related |
273 | 273 | obj.related_select_cols = [] |
274 | | obj.aggregates = copy.deepcopy(self.aggregates, memo=memo) |
| 274 | obj.aggregates = SortedDict((k, v.clone()) |
| 275 | for k, v in self.aggregates.items()) |
275 | 276 | if self.aggregate_select_mask is None: |
276 | 277 | obj.aggregate_select_mask = None |
277 | 278 | else: |
… |
… |
class Query(object):
|
294 | 295 | obj._extra_select_cache = self._extra_select_cache.copy() |
295 | 296 | obj.extra_tables = self.extra_tables |
296 | 297 | obj.extra_order_by = self.extra_order_by |
297 | | obj.deferred_loading = copy.deepcopy(self.deferred_loading, memo=memo) |
| 298 | obj.deferred_loading = copy.copy(self.deferred_loading[0]), self.deferred_loading[1] |
298 | 299 | if self.filter_is_sticky and self.used_aliases: |
299 | 300 | obj.used_aliases = self.used_aliases.copy() |
300 | 301 | else: |
… |
… |
class Query(object):
|
508 | 509 | # Now relabel a copy of the rhs where-clause and add it to the current |
509 | 510 | # one. |
510 | 511 | if rhs.where: |
511 | | w = copy.deepcopy(rhs.where) |
| 512 | w = rhs.where.clone() |
512 | 513 | w.relabel_aliases(change_map) |
513 | 514 | if not self.where: |
514 | 515 | # Since 'self' matches everything, add an explicit "include |
… |
… |
class Query(object):
|
529 | 530 | if isinstance(col, (list, tuple)): |
530 | 531 | self.select.append((change_map.get(col[0], col[0]), col[1])) |
531 | 532 | else: |
532 | | item = copy.deepcopy(col) |
| 533 | item = col.clone() |
533 | 534 | item.relabel_aliases(change_map) |
534 | 535 | self.select.append(item) |
535 | 536 | self.select_fields = rhs.select_fields[:] |
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py
index 1455ba6..bcf0c3b 100644
a
|
b
|
class EverythingNode(object):
|
263 | 263 | def relabel_aliases(self, change_map, node=None): |
264 | 264 | return |
265 | 265 | |
| 266 | def clone(self): |
| 267 | return self |
| 268 | |
266 | 269 | class NothingNode(object): |
267 | 270 | """ |
268 | 271 | A node that matches nothing. |
… |
… |
class NothingNode(object):
|
273 | 276 | def relabel_aliases(self, change_map, node=None): |
274 | 277 | return |
275 | 278 | |
| 279 | def clone(self): |
| 280 | return self |
| 281 | |
276 | 282 | class ExtraWhere(object): |
277 | 283 | def __init__(self, sqls, params): |
278 | 284 | self.sqls = sqls |
… |
… |
class ExtraWhere(object):
|
281 | 287 | def as_sql(self, qn=None, connection=None): |
282 | 288 | return " AND ".join(self.sqls), tuple(self.params or ()) |
283 | 289 | |
| 290 | def clone(self): |
| 291 | return self |
| 292 | |
284 | 293 | class Constraint(object): |
285 | 294 | """ |
286 | 295 | An object that can be passed to WhereNode.add() and knows how to |
… |
… |
class Constraint(object):
|
345 | 354 | def relabel_aliases(self, change_map): |
346 | 355 | if self.alias in change_map: |
347 | 356 | self.alias = change_map[self.alias] |
| 357 | |
| 358 | def clone(self): |
| 359 | return Constraint(self.alias, self.col, self.field) |
diff --git a/django/utils/tree.py b/django/utils/tree.py
index 36b5977..758bc46 100644
a
|
b
|
class Node(object):
|
45 | 45 | return obj |
46 | 46 | _new_instance = classmethod(_new_instance) |
47 | 47 | |
| 48 | def clone(self): |
| 49 | """ |
| 50 | Creates a clone of the tree. Must only be called on root nodes (nodes |
| 51 | with empty subtree_parents). |
| 52 | """ |
| 53 | assert not self.subtree_parents, '.clone() can only be called on root nodes' |
| 54 | clone = self.__class__._new_instance( |
| 55 | children=[], connector=self.connector, negated=self.negated) |
| 56 | for child in self.children: |
| 57 | # A pretty bad abstraction leak: the "tuple" child is a special |
| 58 | # child type used in querysets. Querysets should instead define |
| 59 | # a leafnode which does the two lines below the check. (TODO). |
| 60 | if isinstance(child, tuple): |
| 61 | clone.children.append( |
| 62 | (child[0].clone(), child[1], child[2], child[3])) |
| 63 | else: |
| 64 | clone.children.append(child.clone()) |
| 65 | return clone |
| 66 | |
48 | 67 | def __str__(self): |
49 | 68 | if self.negated: |
50 | 69 | return '(NOT (%s: %s))' % (self.connector, ', '.join([str(c) for c |