Ticket #7539: 7539.on_delete.r11706.diff
File 7539.on_delete.r11706.diff, 26.3 KB (added by , 15 years ago) |
---|
-
tests/modeltests/on_delete/tests.py
1 from django.test import TestCase 2 from django.db import models, IntegrityError 3 4 class R(models.Model): 5 is_default = models.BooleanField(default=False) 6 7 def __str__(self): 8 return "%s" % self.pk 9 10 get_default_r = lambda: R.objects.get_or_create(is_default=True)[0] 11 12 class S(models.Model): 13 r = models.ForeignKey(R) 14 15 class T(models.Model): 16 s = models.ForeignKey(S) 17 18 class A(models.Model): 19 name = models.CharField(max_length=10) 20 21 auto = models.ForeignKey(R, related_name="auto_set") 22 auto_nullable = models.ForeignKey(R, null=True, related_name='auto_nullable_set') 23 setnull = models.ForeignKey(R, on_delete=models.SET_NULL, null=True, related_name='setnull_set') 24 setdefault = models.ForeignKey(R, on_delete=models.SET_DEFAULT, default=get_default_r, related_name='setdefault_set') 25 setdefault_none = models.ForeignKey(R, on_delete=models.SET_DEFAULT, default=None, null=True, related_name='setnull_nullable_set') 26 cascade = models.ForeignKey(R, on_delete=models.CASCADE, related_name='cascade_set') 27 cascade_nullable = models.ForeignKey(R, on_delete=models.CASCADE, null=True, related_name='cascade_nullable_set') 28 protect = models.ForeignKey(R, on_delete=models.PROTECT, null=True) 29 donothing = models.ForeignKey(R, on_delete=models.DO_NOTHING, null=True, related_name='donothing_set') 30 31 def create_a(name): 32 a = A(name=name) 33 for name in ('auto', 'auto_nullable', 'setnull', 'setdefault', 'setdefault_none', 'cascade', 'cascade_nullable', 'protect', 'donothing'): 34 r = R.objects.create() 35 setattr(a, name, r) 36 a.save() 37 return a 38 39 class OnDeleteTests(TestCase): 40 def test_basics(self): 41 DEFAULT = get_default_r() 42 43 a = create_a('auto') 44 a.auto.delete() 45 self.failIf(A.objects.filter(name='auto').exists()) 46 47 a = create_a('auto_nullable') 48 a.auto_nullable.delete() 49 self.failUnlessEqual([a], list(A.objects.all())) 50 51 a = create_a('setnull') 52 a.setnull.delete() 53 a = A.objects.get(pk=a.pk) 54 self.failUnlessEqual(None, a.setnull) 55 56 a = create_a('setdefault') 57 a.setdefault.delete() 58 a = A.objects.get(pk=a.pk) 59 self.failUnlessEqual(DEFAULT, a.setdefault) 60 61 a = create_a('setdefault_none') 62 a.setdefault_none.delete() 63 a = A.objects.get(pk=a.pk) 64 self.failUnlessEqual(None, a.setdefault_none) 65 66 a = create_a('cascade') 67 a.cascade.delete() 68 self.failIf(A.objects.filter(name='cascade').exists()) 69 70 a = create_a('cascade_nullable') 71 a.cascade_nullable.delete() 72 self.failIf(A.objects.filter(name='cascade_nullable').exists()) 73 74 a = create_a('protect') 75 self.assertRaises(IntegrityError, a.protect.delete) 76 77 # Testing DO_NOTHING is a bit harder: It would raise IntegrityError for a normal model, 78 # so we connect to pre_delete and set the fk to a known value. 79 replacement_r = R.objects.create() 80 def check_do_nothing(sender, **kwargs): 81 obj = kwargs['instance'] 82 obj.donothing_set.update(donothing=replacement_r) 83 models.signals.pre_delete.connect(check_do_nothing) 84 a = create_a('do_nothing') 85 a.donothing.delete() 86 a = A.objects.get(pk=a.pk) 87 self.failUnlessEqual(replacement_r, a.donothing) 88 models.signals.pre_delete.disconnect(check_do_nothing) 89 90 # cleanup 91 A.objects.all().update(protect=None, donothing=None) 92 R.objects.all().delete() 93 self.failIf(A.objects.exists()) 94 95 def test_cache_update(self): 96 deleted = [] 97 related_setnull_sets = [] 98 def pre_delete(sender, **kwargs): 99 obj = kwargs['instance'] 100 deleted.append(obj) 101 if isinstance(obj, R): 102 related_setnull_sets.append(list(a.pk for a in obj.setnull_set.all())) 103 104 models.signals.pre_delete.connect(pre_delete) 105 a = create_a('cache_update_setnull') 106 a.setnull.delete() 107 108 a = create_a('cache_update_setnull') 109 a.cascade.delete() 110 111 for obj in deleted: 112 self.failUnlessEqual(None, obj.pk) 113 114 for pk_list in related_setnull_sets: 115 for a in A.objects.filter(id__in=pk_list): 116 self.failUnlessEqual(None, a.setnull) 117 118 models.signals.pre_delete.disconnect(pre_delete) 119 120 def test_deletion_order(self): 121 pre_delete_order = [] 122 post_delete_order = [] 123 124 def log_post_delete(sender, **kwargs): 125 pre_delete_order.append((sender, kwargs['instance'].pk)) 126 127 def log_pre_delete(sender, **kwargs): 128 post_delete_order.append((sender, kwargs['instance'].pk)) 129 130 models.signals.post_delete.connect(log_post_delete) 131 models.signals.pre_delete.connect(log_pre_delete) 132 133 r = R.objects.create(pk=1) 134 s1 = S.objects.create(pk=1, r=r) 135 s2 = S.objects.create(pk=2, r=r) 136 t1 = T.objects.create(pk=1, s=s1) 137 t2 = T.objects.create(pk=2, s=s2) 138 r.delete() 139 self.failUnlessEqual(pre_delete_order, [(T, 2), (T, 1), (S, 2), (S, 1), (R, 1)]) 140 self.failUnlessEqual(post_delete_order, [(T, 1), (T, 2), (S, 1), (S, 2), (R, 1)]) 141 142 models.signals.post_delete.disconnect(log_post_delete) 143 models.signals.post_delete.disconnect(log_pre_delete) 144 -
tests/modeltests/delete/models.py
44 44 __test__ = {'API_TESTS': """ 45 45 ### Tests for models A,B,C,D ### 46 46 47 ## First, test the CollectedObjects data structure directly48 49 >>> from django.db.models.query import CollectedObjects50 51 >>> g = CollectedObjects()52 >>> g.add("key1", 1, "item1", None)53 False54 >>> g["key1"]55 {1: 'item1'}56 >>> g.add("key2", 1, "item1", "key1")57 False58 >>> g.add("key2", 2, "item2", "key1")59 False60 >>> g["key2"]61 {1: 'item1', 2: 'item2'}62 >>> g.add("key3", 1, "item1", "key1")63 False64 >>> g.add("key3", 1, "item1", "key2")65 True66 >>> g.ordered_keys()67 ['key3', 'key2', 'key1']68 69 >>> g.add("key2", 1, "item1", "key3")70 True71 >>> g.ordered_keys()72 Traceback (most recent call last):73 ...74 CyclicDependency: There is a cyclic dependency of items to be processed.75 76 77 ## Second, test the usage of CollectedObjects by Model.delete()78 79 47 # Due to the way that transactions work in the test harness, 80 48 # doing m.delete() here can work but fail in a real situation, 81 49 # since it may delete all objects, but not in the right order. … … 111 79 >>> c1.save() 112 80 >>> d1 = D(c=c1, a=a1) 113 81 >>> d1.save() 114 115 >>> o = CollectedObjects()116 >>> a1._collect_sub_objects(o)117 >>> o.keys()118 [<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]119 82 >>> a1.delete() 120 83 121 84 # Same again with a known bad order … … 130 93 >>> c2.save() 131 94 >>> d2 = D(c=c2, a=a2) 132 95 >>> d2.save() 133 134 >>> o = CollectedObjects()135 >>> a2._collect_sub_objects(o)136 >>> o.keys()137 [<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]138 96 >>> a2.delete() 139 97 140 98 ### Tests for models E,F - nullable related fields ### 141 99 142 ## First, test the CollectedObjects data structure directly143 144 >>> g = CollectedObjects()145 >>> g.add("key1", 1, "item1", None)146 False147 >>> g.add("key2", 1, "item1", "key1", nullable=True)148 False149 >>> g.add("key1", 1, "item1", "key2")150 True151 >>> g.ordered_keys()152 ['key1', 'key2']153 154 ## Second, test the usage of CollectedObjects by Model.delete()155 156 100 >>> e1 = E() 157 101 >>> e1.save() 158 102 >>> f1 = F(e=e1) 159 103 >>> f1.save() 160 104 >>> e1.f = f1 161 105 >>> e1.save() 162 163 # Since E.f is nullable, we should delete F first (after nulling out164 # the E.f field), then E.165 166 >>> o = CollectedObjects()167 >>> e1._collect_sub_objects(o)168 >>> o.keys()169 [<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]170 171 # temporarily replace the UpdateQuery class to verify that E.f is actually nulled out first172 >>> import django.db.models.sql173 >>> class LoggingUpdateQuery(django.db.models.sql.UpdateQuery):174 ... def clear_related(self, related_field, pk_list):175 ... print "CLEARING FIELD",related_field.name176 ... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list)177 >>> original_class = django.db.models.sql.UpdateQuery178 >>> django.db.models.sql.UpdateQuery = LoggingUpdateQuery179 106 >>> e1.delete() 180 CLEARING FIELD f181 107 182 108 >>> e2 = E() 183 109 >>> e2.save() … … 185 111 >>> f2.save() 186 112 >>> e2.f = f2 187 113 >>> e2.save() 188 189 # Same deal as before, though we are starting from the other object.190 191 >>> o = CollectedObjects()192 >>> f2._collect_sub_objects(o)193 >>> o.keys()194 [<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]195 196 114 >>> f2.delete() 197 CLEARING FIELD f198 199 # Put this back to normal200 >>> django.db.models.sql.UpdateQuery = original_class201 115 """ 202 116 } -
django/db/models/base.py
523 523 (model_class, {pk_val: obj, pk_val: obj, ...}), ...] 524 524 """ 525 525 pk_val = self._get_pk_val() 526 if seen_objs.add(self .__class__, pk_val, self, parent, nullable):526 if seen_objs.add(self, parent, nullable): 527 527 return 528 528 529 529 for related in self._meta.get_all_related_objects(): … … 534 534 except ObjectDoesNotExist: 535 535 pass 536 536 else: 537 sub_obj._collect_sub_objects(seen_objs, self.__class__, related.field.null)537 related.field.rel.on_delete(self, related, sub_obj, seen_objs) 538 538 else: 539 539 # To make sure we can access all elements, we can't use the 540 540 # normal manager on the related object. So we work directly … … 547 547 raise AssertionError("Should never get here.") 548 548 delete_qs = rel_descriptor.delete_manager(self).all() 549 549 for sub_obj in delete_qs: 550 sub_obj._collect_sub_objects(seen_objs, self.__class__, related.field.null)550 related.field.rel.on_delete(self, related, sub_obj, seen_objs) 551 551 552 552 # Handle any ancestors (for the model-inheritance case). We do this by 553 553 # traversing to the most remote parent classes -- those with no parents -
django/db/models/fields/related.py
1 from django.db import connection, transaction 1 from django.db import connection, transaction, IntegrityError 2 2 from django.db.backends import util 3 3 from django.db.models import signals, get_model 4 4 from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist … … 18 18 19 19 RECURSIVE_RELATIONSHIP_CONSTANT = 'self' 20 20 21 def CASCADE(obj, related, sub_obj, seen_objs): 22 sub_obj._collect_sub_objects(seen_objs, obj.__class__) 23 24 def PROTECT(obj, related, sub_obj, seen_objs): 25 msg = '[Django] Cannot delete a parent object: a foreign key constraint fails (ForeignKey `%s` (pk=`%s`) references `%s` (pk=`%s`))' % ( 26 sub_obj.__class__, sub_obj.pk, 27 obj.__class__, obj.pk, 28 ) 29 raise IntegrityError(msg) 30 31 def SET(value): 32 def set_on_delete(obj, related, sub_obj, seen_objs): 33 seen_objs.add(sub_obj, related.field, value) 34 return set_on_delete 35 36 def SET_NULL(obj, related, sub_obj, seen_objs): 37 seen_objs.add_field_update(sub_obj, related.field, None) 38 39 def SET_DEFAULT(obj, related, sub_obj, seen_objs): 40 seen_objs.add_field_update(sub_obj, related.field, related.field.get_default()) 41 42 def DO_NOTHING(obj, related, sub_obj, seen_objs): 43 pass 44 45 21 46 pending_lookups = {} 22 47 23 48 def add_lazy_relation(cls, field, relation, operation): … … 628 653 629 654 class ManyToOneRel(object): 630 655 def __init__(self, to, field_name, related_name=None, 631 limit_choices_to=None, lookup_overrides=None, parent_link=False): 656 limit_choices_to=None, lookup_overrides=None, parent_link=False, 657 on_delete=None): 632 658 try: 633 659 to._meta 634 660 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT … … 641 667 self.lookup_overrides = lookup_overrides or {} 642 668 self.multiple = True 643 669 self.parent_link = parent_link 670 self.on_delete = on_delete 644 671 645 672 def get_related_field(self): 646 673 """ … … 655 682 656 683 class OneToOneRel(ManyToOneRel): 657 684 def __init__(self, to, field_name, related_name=None, 658 limit_choices_to=None, lookup_overrides=None, parent_link=False): 685 limit_choices_to=None, lookup_overrides=None, parent_link=False, 686 on_delete=None): 659 687 super(OneToOneRel, self).__init__(to, field_name, 660 688 related_name=related_name, limit_choices_to=limit_choices_to, 661 lookup_overrides=lookup_overrides, parent_link=parent_link) 689 lookup_overrides=lookup_overrides, parent_link=parent_link, 690 on_delete=on_delete) 662 691 self.multiple = False 663 692 664 693 class ManyToManyRel(object): … … 692 721 assert not to._meta.abstract, "%s cannot define a relation with abstract class %s" % (self.__class__.__name__, to._meta.object_name) 693 722 to_field = to_field or to._meta.pk.name 694 723 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 724 725 on_delete = kwargs.pop('on_delete', None) 726 if on_delete is None: 727 if kwargs.get('null', False): 728 on_delete = SET_NULL 729 else: 730 on_delete = CASCADE 695 731 696 732 kwargs['rel'] = rel_class(to, to_field, 697 733 related_name=kwargs.pop('related_name', None), 698 734 limit_choices_to=kwargs.pop('limit_choices_to', None), 699 735 lookup_overrides=kwargs.pop('lookup_overrides', None), 700 parent_link=kwargs.pop('parent_link', False)) 736 parent_link=kwargs.pop('parent_link', False), 737 on_delete=on_delete) 701 738 Field.__init__(self, **kwargs) 702 739 703 740 self.db_index = True … … 742 779 target = self.rel.to._meta.db_table 743 780 cls._meta.duplicate_targets[self.column] = (target, "o2m") 744 781 782 on_delete = self.rel.on_delete 783 if on_delete == SET_NULL and not self.null: 784 specification = "'on_delete=SET_NULL'" 785 raise ValueError("%s specified for %s '%s.%s', but the field is not nullable." % (specification, type(self).__name__, cls.__name__, name)) 786 if on_delete == SET_DEFAULT and not self.has_default(): 787 specification = "'on_delete=SET_DEFAULT'" 788 raise ValueError("%s specified for %s '%s.%s', but the field has no default value." % (specification, type(self).__name__, cls.__name__, name)) 789 745 790 def contribute_to_related_class(self, cls, related): 746 791 setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) 747 792 -
django/db/models/__init__.py
11 11 from django.db.models.fields.subclassing import SubfieldBase 12 12 from django.db.models.fields.files import FileField, ImageField 13 13 from django.db.models.fields.related import ForeignKey, OneToOneField, ManyToManyField, ManyToOneRel, ManyToManyRel, OneToOneRel 14 from django.db.models.fields.related import CASCADE, PROTECT, SET, SET_NULL, SET_DEFAULT, DO_NOTHING 14 15 from django.db.models import signals 15 16 16 17 # Admin stages. -
django/db/models/query.py
1011 1011 else: 1012 1012 forced_managed = False 1013 1013 try: 1014 ordered_classes = seen_objs.keys() 1015 except CyclicDependency: 1016 # If there is a cyclic dependency, we cannot in general delete the 1017 # objects. However, if an appropriate transaction is set up, or if the 1018 # database is lax enough, it will succeed. So for now, we go ahead and 1019 # try anyway. 1020 ordered_classes = seen_objs.unordered_keys() 1014 try: 1015 seen_objs.sort() 1016 except CyclicDependency: 1017 pass 1021 1018 1022 obj_pairs = {} 1023 try: 1024 for cls in ordered_classes: 1025 items = seen_objs[cls].items() 1026 items.sort() 1027 obj_pairs[cls] = items 1019 for obj in seen_objs: 1020 signals.pre_delete.send(sender=obj.__class__, instance=obj) 1021 1022 seen_objs.execute_update() 1028 1023 1029 # Pre-notify all instances to be deleted. 1030 for pk_val, instance in items: 1031 signals.pre_delete.send(sender=cls, instance=instance) 1024 seen_objs.execute_delete_related() 1025 1026 seen_objs.reverse_instances() 1027 1028 seen_objs.execute_delete() 1032 1029 1033 pk_list = [pk for pk,instance in items] 1034 del_query = sql.DeleteQuery(cls, connection) 1035 del_query.delete_batch_related(pk_list) 1030 for obj in seen_objs: 1031 signals.post_delete.send(sender=obj.__class__, instance=obj) 1032 1033 seen_objs.update_instances() 1036 1034 1037 update_query = sql.UpdateQuery(cls, connection)1038 for field, model in cls._meta.get_fields_with_model():1039 if (field.rel and field.null and field.rel.to in seen_objs and1040 filter(lambda f: f.column == field.rel.get_related_field().column,1041 field.rel.to._meta.fields)):1042 if model:1043 sql.UpdateQuery(model, connection).clear_related(field,1044 pk_list)1045 else:1046 update_query.clear_related(field, pk_list)1047 1048 # Now delete the actual data.1049 for cls in ordered_classes:1050 items = obj_pairs[cls]1051 items.reverse()1052 1053 pk_list = [pk for pk,instance in items]1054 del_query = sql.DeleteQuery(cls, connection)1055 del_query.delete_batch(pk_list)1056 1057 # Last cleanup; set NULLs where there once was a reference to the1058 # object, NULL the primary key of the found objects, and perform1059 # post-notification.1060 for pk_val, instance in items:1061 for field in cls._meta.fields:1062 if field.rel and field.null and field.rel.to in seen_objs:1063 setattr(instance, field.attname, None)1064 1065 signals.post_delete.send(sender=cls, instance=instance)1066 setattr(instance, cls._meta.pk.attname, None)1067 1068 1035 if forced_managed: 1069 1036 transaction.commit() 1070 1037 else: -
django/db/models/query_utils.py
39 39 """ 40 40 41 41 def __init__(self, previously_seen=None): 42 # {model: {(field, value): set([instances])}} 43 self.field_updates = {} 42 44 self.data = {} 43 45 self.children = {} 44 46 if previously_seen: 45 self.blocked = previously_seen.blocked 46 for cls, seen in previously_seen.data.items(): 47 self.blocked.setdefault(cls, SortedDict()).update(seen) 47 self.blocked = set(previously_seen) 48 self.blocked.update(previously_seen.blocked) 48 49 else: 49 self.blocked = {}50 self.blocked = set() 50 51 51 def add(self, model, pk,obj, parent_model, nullable=False):52 def add(self, obj, parent_model, nullable=False): 52 53 """ 53 54 Adds an item to the container. 54 55 55 56 Arguments: 56 * model - the class of the object being added.57 * pk - the primary key.58 57 * obj - the object itself. 59 58 * parent_model - the model of the parent object that this object was 60 59 reached through. … … 62 61 63 62 Returns True if the item already existed in the structure and 64 63 False otherwise. 65 """ 66 if pk in self.blocked.get(model, {}):64 """ 65 if obj in self.blocked: 67 66 return True 68 67 69 d = self.data.setdefault(model, SortedDict()) 70 retval = pk in d 71 d[pk] = obj 68 model = obj.__class__ 69 instances = self.data.setdefault(model, []) 70 seen_before = obj in instances 71 if not seen_before: 72 instances.append(obj) 72 73 # Nullable relationships can be ignored -- they are nulled out before 73 74 # deleting, and therefore do not affect the order in which objects 74 75 # have to be deleted. 75 76 if parent_model is not None and not nullable: 76 self.children.setdefault(parent_model, []).append(model) 77 return retval 77 self.children.setdefault(parent_model, set()).add(model) 78 return seen_before 79 80 def add_field_update(self, obj, field, value): 81 updates = self.field_updates.setdefault(obj.__class__, dict()) 82 updates.setdefault((field, value), set()).add(obj) 83 84 def __iter__(self): 85 for instances in self.data.itervalues(): 86 for obj in instances: 87 yield obj 78 88 79 def __contains__(self, key):80 return self.data.__contains__(key)81 82 def __getitem__(self, key):83 return self.data[key]84 85 89 def __nonzero__(self): 86 90 return bool(self.data) 87 91 88 def iteritems(self): 89 for k in self.ordered_keys(): 90 yield k, self[k] 91 92 def items(self): 93 return list(self.iteritems()) 94 95 def keys(self): 96 return self.ordered_keys() 97 98 def ordered_keys(self): 99 """ 100 Returns the models in the order that they should be dealt with (i.e. 101 models with no dependencies first). 102 """ 103 dealt_with = SortedDict() 104 # Start with items that have no children 92 def sort(self): 93 for instances in self.data.itervalues(): 94 instances.sort(key=lambda obj: obj.pk) 95 96 sorted_models = [] 105 97 models = self.data.keys() 106 while len( dealt_with) < len(models):98 while len(sorted_models) < len(models): 107 99 found = False 108 100 for model in models: 109 if model in dealt_with:101 if model in sorted_models: 110 102 continue 111 children = self.children. setdefault(model, [])112 if len([c for c in children if c not in dealt_with]) == 0:113 dealt_with[model] = None103 children = self.children.get(model) 104 if not children or not children.difference(sorted_models): 105 sorted_models.append(model) 114 106 found = True 115 107 if not found: 116 raise CyclicDependency( 117 "There is a cyclic dependency of items to be processed.")108 raise CyclicDependency() 109 self.data = SortedDict([(model, self.data[model]) for model in sorted_models]) 118 110 119 return dealt_with.keys() 111 def reverse_instances(self): 112 for instances in self.data.itervalues(): 113 instances.reverse() 120 114 121 def unordered_keys(self): 122 """ 123 Fallback for the case where is a cyclic dependency but we don't care. 124 """ 125 return self.data.keys() 115 def execute_delete_related(self): 116 from django.db.models import connection, sql 117 for model, instances in self.data.iteritems(): 118 pk_list = [obj.pk for obj in instances] 119 del_query = sql.DeleteQuery(model, connection) 120 del_query.delete_batch_related(pk_list) 126 121 122 def execute_delete(self): 123 from django.db.models import connection, sql 124 for model, instances in self.data.iteritems(): 125 pk_list = [obj.pk for obj in instances] 126 del_query = sql.DeleteQuery(model, connection) 127 del_query.delete_batch(pk_list) 128 129 def execute_update(self): 130 from django.db.models import connection, sql 131 from django.db.models.sql.constants import GET_ITERATOR_CHUNK_SIZE 132 for model, instances_for_fieldvalues in self.field_updates.iteritems(): 133 for (field, value), instances in instances_for_fieldvalues.iteritems(): 134 pk_field = model._meta.pk 135 pk_list = [obj.pk for obj in instances] 136 for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE): 137 query = sql.UpdateQuery(model, connection) 138 query.where = query.where_class() 139 query.where.add((sql.where.Constraint(None, pk_field.column, pk_field), 'in', pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]), sql.where.AND) 140 query.add_update_values({field.name: value}) 141 query.execute_sql() 142 143 def update_instances(self): 144 for model, instances_for_fieldvalues in self.field_updates.iteritems(): 145 for (field, value), instances in instances_for_fieldvalues.iteritems(): 146 for obj in instances: 147 setattr(obj, field.attname, value) 148 for model, instances in self.data.iteritems(): 149 for instance in instances: 150 setattr(instance, model._meta.pk.attname, None) 151 152 127 153 class QueryWrapper(object): 128 154 """ 129 155 A type that indicates the contents are an SQL fragment and the associate