Ticket #16759: #16759-remove_deepcopy_in_qs.diff
File #16759-remove_deepcopy_in_qs.diff, 9.4 KB (added by , 12 years ago) |
---|
-
django/db/models/sql/expressions.py
diff --git a/django/db/models/sql/expressions.py b/django/db/models/sql/expressions.py index 1bbf742..51061ff 100644
1 1 from django.core.exceptions import FieldError 2 2 from django.db.models.fields import FieldDoesNotExist 3 3 from django.db.models.sql.constants import LOOKUP_SEP 4 import copy 4 5 5 6 class SQLEvaluator(object): 6 7 def __init__(self, expression, query, allow_joins=True): … … def __init__(self, expression, query, allow_joins=True, reuse=None): 12 13 self.contains_aggregate = False 13 14 self.expression.prepare(self, query, allow_joins) 14 15 16 def clone(self): 17 clone = copy.copy(self) 18 clone.cols = {} 19 for key, col in self.cols.items(): 20 if hasattr(col, 'clone'): 21 clone.cols[key] = col.clone() 22 else: 23 clone.cols[key] = col 24 return clone 25 26 15 27 def prepare(self): 16 28 return self 17 29 -
django/db/models/sql/aggregates.py
diff --git a/django/db/models/sql/aggregates.py b/django/db/models/sql/aggregates.py index b41314a..75a330f 100644
1 1 """ 2 2 Classes to represent the default SQL aggregate functions 3 3 """ 4 import copy 4 5 5 6 from django.db.models.fields import IntegerField, FloatField 6 7 … … def __init__(self, col, source=None, is_summary=False, **extra): 62 63 63 64 self.field = tmp 64 65 66 def clone(self): 67 # Different aggregates have different init methods, so use copy here 68 # deepcopy is not needed, as self.col is only changing variable. 69 return copy.copy(self) 70 65 71 def relabel_aliases(self, change_map): 66 72 if isinstance(self.col, (list, tuple)): 67 73 self.col = (change_map.get(self.col[0], self.col[0]), self.col[1]) -
django/db/models/sql/query.py
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 7f331bf..449404a 100644
def clone(self, klass=None, memo=None, **kwargs): 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 … … def clone(self, klass=None, memo=None, **kwargs): 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: … … def clone(self, klass=None, memo=None, **kwargs): 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: … … def combine(self, rhs, connector): 509 510 # Now relabel a copy of the rhs where-clause and add it to the current 510 511 # one. 511 512 if rhs.where: 512 w = copy.deepcopy(rhs.where)513 w = rhs.where.clone() 513 514 w.relabel_aliases(change_map) 514 515 if not self.where: 515 516 # Since 'self' matches everything, add an explicit "include … … def combine(self, rhs, connector): 530 531 if isinstance(col, (list, tuple)): 531 532 self.select.append((change_map.get(col[0], col[0]), col[1])) 532 533 else: 533 item = co py.deepcopy(col)534 item = col.clone() 534 535 item.relabel_aliases(change_map) 535 536 self.select.append(item) 536 537 self.select_fields = rhs.select_fields[:] -
django/db/models/sql/where.py
diff --git a/django/db/models/sql/where.py b/django/db/models/sql/where.py index 5515bc4..f9475a6 100644
10 10 11 11 from django.utils import tree 12 12 from django.db.models.fields import Field 13 from django.db.models.sql.datastructures import EmptyResultSet, FullResultSet 13 from django.db.models.sql.datastructures import EmptyResultSet, FullResultSet, Empty 14 14 from django.db.models.sql.aggregates import Aggregate 15 15 16 16 # Connection types … … def relabel_aliases(self, change_map, node=None): 254 254 # Check if the query value also requires relabelling 255 255 if hasattr(child[3], 'relabel_aliases'): 256 256 child[3].relabel_aliases(change_map) 257 258 def clone(self): 259 """ 260 Creates a clone of the tree. Must only be called on root nodes (nodes 261 with empty subtree_parents). Childs must be either Contraint, lookup, 262 value tuples, or objects supporting .clone(). 263 """ 264 assert not self.subtree_parents, '.clone() can only be called on root nodes' 265 clone = self.__class__._new_instance( 266 children=[], connector=self.connector, negated=self.negated) 267 for child in self.children: 268 if isinstance(child, tuple): 269 clone.children.append( 270 tuple(map(lambda o: o.clone() if hasattr(o, 'clone') else o, child))) 271 else: 272 clone.children.append(child.clone()) 273 return clone 257 274 258 275 class EverythingNode(object): 259 276 """ … … def as_sql(self, qn=None, connection=None): 266 283 def relabel_aliases(self, change_map, node=None): 267 284 return 268 285 286 def clone(self): 287 return self 288 269 289 class NothingNode(object): 270 290 """ 271 291 A node that matches nothing. … … def as_sql(self, qn=None, connection=None): 276 296 def relabel_aliases(self, change_map, node=None): 277 297 return 278 298 299 def clone(self): 300 return self 301 279 302 class ExtraWhere(object): 280 303 def __init__(self, sqls, params): 281 304 self.sqls = sqls … … def as_sql(self, qn=None, connection=None): 285 308 sqls = ["(%s)" % sql for sql in self.sqls] 286 309 return " AND ".join(sqls), tuple(self.params or ()) 287 310 311 def clone(self): 312 return self 313 288 314 class Constraint(object): 289 315 """ 290 316 An object that can be passed to WhereNode.add() and knows how to … … def process(self, lookup_type, value, connection): 349 375 def relabel_aliases(self, change_map): 350 376 if self.alias in change_map: 351 377 self.alias = change_map[self.alias] 378 379 def clone(self): 380 new = Empty() 381 new.__class__ = self.__class__ 382 new.alias, new.col, new.field = self.alias, self.col, self.field 383 return new -
tests/regressiontests/queries/tests.py
diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index 1035a38..9ef06ca 100644
def test_sliced_delete(self): 1649 1649 1650 1650 1651 1651 class CloneTests(TestCase): 1652 1652 1653 def test_evaluated_queryset_as_argument(self): 1653 1654 "#13227 -- If a queryset is already evaluated, it can still be used as a query arg" 1654 1655 n = Note(note='Test1', misc='misc') … … def test_evaluated_queryset_as_argument(self): 1666 1667 except: 1667 1668 self.fail('Query should be clonable') 1668 1669 1670 def test_no_model_options_cloning(self): 1671 """ 1672 Test that cloning a queryset does not get out of hand. While complete 1673 testing is impossible, this is a sanity check against invalid use of 1674 deepcopy. refs #16759. 1675 """ 1676 opts_class = type(Note._meta) 1677 note_deepcopy = getattr(opts_class, "__deepcopy__", None) 1678 opts_class.__deepcopy__ = lambda obj, memo: self.fail("Model options shouldn't be cloned.") 1679 try: 1680 Note.objects.filter(pk__lte=F('pk') + 1).all() 1681 finally: 1682 if note_deepcopy is None: 1683 delattr(opts_class, "__deepcopy__") 1684 else: 1685 opts_class.__deepcopy__ = note_deepcopy 1686 1687 def test_no_fields_cloning(self): 1688 """ 1689 Test that cloning a queryset does not get out of hand. While complete 1690 testing is impossible, this is a sanity check against invalid use of 1691 deepcopy. refs #16759. 1692 """ 1693 opts_class = type(Note._meta.get_field_by_name("misc")[0]) 1694 note_deepcopy = getattr(opts_class, "__deepcopy__", None) 1695 opts_class.__deepcopy__ = lambda obj, memo: self.fail("Model fields shouldn't be cloned") 1696 try: 1697 Note.objects.filter(note=F('misc')).all() 1698 finally: 1699 if note_deepcopy is None: 1700 delattr(opts_class, "__deepcopy__") 1701 else: 1702 opts_class.__deepcopy__ = note_deepcopy 1669 1703 1670 1704 class EmptyQuerySetTests(TestCase): 1671 1705 def test_emptyqueryset_values(self):