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..631fc6b 100644
a
|
b
|
class WhereNode(tree.Node):
|
251 | 251 | # Check if the query value also requires relabelling |
252 | 252 | if hasattr(child[3], 'relabel_aliases'): |
253 | 253 | child[3].relabel_aliases(change_map) |
| 254 | |
| 255 | def clone(self): |
| 256 | """ |
| 257 | Creates a clone of the tree. Must only be called on root nodes (nodes |
| 258 | with empty subtree_parents). Childs must be either Contraint, lookup, |
| 259 | value tuples, or objects supporting .clone(). |
| 260 | """ |
| 261 | assert not self.subtree_parents, '.clone() can only be called on root nodes' |
| 262 | clone = self.__class__._new_instance( |
| 263 | children=[], connector=self.connector, negated=self.negated) |
| 264 | for child in self.children: |
| 265 | if isinstance(child, tuple): |
| 266 | clone.children.append( |
| 267 | (child[0].clone(), child[1], child[2], child[3])) |
| 268 | else: |
| 269 | clone.children.append(child.clone()) |
| 270 | return clone |
254 | 271 | |
255 | 272 | class EverythingNode(object): |
256 | 273 | """ |
… |
… |
class EverythingNode(object):
|
263 | 280 | def relabel_aliases(self, change_map, node=None): |
264 | 281 | return |
265 | 282 | |
| 283 | def clone(self): |
| 284 | return self |
| 285 | |
266 | 286 | class NothingNode(object): |
267 | 287 | """ |
268 | 288 | A node that matches nothing. |
… |
… |
class NothingNode(object):
|
273 | 293 | def relabel_aliases(self, change_map, node=None): |
274 | 294 | return |
275 | 295 | |
| 296 | def clone(self): |
| 297 | return self |
| 298 | |
276 | 299 | class ExtraWhere(object): |
277 | 300 | def __init__(self, sqls, params): |
278 | 301 | self.sqls = sqls |
… |
… |
class ExtraWhere(object):
|
281 | 304 | def as_sql(self, qn=None, connection=None): |
282 | 305 | return " AND ".join(self.sqls), tuple(self.params or ()) |
283 | 306 | |
| 307 | def clone(self): |
| 308 | return self |
| 309 | |
284 | 310 | class Constraint(object): |
285 | 311 | """ |
286 | 312 | An object that can be passed to WhereNode.add() and knows how to |
… |
… |
class Constraint(object):
|
345 | 371 | def relabel_aliases(self, change_map): |
346 | 372 | if self.alias in change_map: |
347 | 373 | self.alias = change_map[self.alias] |
| 374 | |
| 375 | def clone(self): |
| 376 | return Constraint(self.alias, self.col, self.field) |