Ticket #7539: 7539.on_delete.r11706.diff
| File 7539.on_delete.r11706.diff, 26.3 kB (added by emulbreh, 5 months ago) |
|---|
-
tests/modeltests/on_delete/tests.py
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
old new 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
