~ubuntu-branches/ubuntu/saucy/python-django/saucy-updates

« back to all changes in this revision

Viewing changes to django/db/models/deletion.py

  • Committer: Package Import Robot
  • Author(s): Luke Faraone, Jakub Wilk, Luke Faraone
  • Date: 2013-05-09 15:10:47 UTC
  • mfrom: (1.1.21) (4.4.27 sid)
  • Revision ID: package-import@ubuntu.com-20130509151047-aqv8d71oj9wvcv8c
Tags: 1.5.1-2
[ Jakub Wilk ]
* Use canonical URIs for Vcs-* fields.

[ Luke Faraone ]
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
from django.db import connections, transaction, IntegrityError
5
5
from django.db.models import signals, sql
6
6
from django.utils.datastructures import SortedDict
 
7
from django.utils import six
7
8
 
8
9
 
9
10
class ProtectedError(IntegrityError):
76
77
        self.data = {}
77
78
        self.batches = {} # {model: {field: set([instances])}}
78
79
        self.field_updates = {} # {model: {(field, value): set([instances])}}
 
80
        # fast_deletes is a list of queryset-likes that can be deleted without
 
81
        # fetching the objects into memory.
 
82
        self.fast_deletes = [] 
79
83
 
80
84
        # Tracks deletion-order dependency for databases without transactions
81
85
        # or ability to defer constraint checks. Only concrete model classes
130
134
            model, {}).setdefault(
131
135
            (field, value), set()).update(objs)
132
136
 
 
137
    def can_fast_delete(self, objs, from_field=None):
 
138
        """
 
139
        Determines if the objects in the given queryset-like can be
 
140
        fast-deleted. This can be done if there are no cascades, no
 
141
        parents and no signal listeners for the object class.
 
142
 
 
143
        The 'from_field' tells where we are coming from - we need this to
 
144
        determine if the objects are in fact to be deleted. Allows also
 
145
        skipping parent -> child -> parent chain preventing fast delete of
 
146
        the child.
 
147
        """
 
148
        if from_field and from_field.rel.on_delete is not CASCADE:
 
149
            return False
 
150
        if not (hasattr(objs, 'model') and hasattr(objs, '_raw_delete')):
 
151
            return False
 
152
        model = objs.model
 
153
        if (signals.pre_delete.has_listeners(model)
 
154
                or signals.post_delete.has_listeners(model)
 
155
                or signals.m2m_changed.has_listeners(model)):
 
156
            return False
 
157
        # The use of from_field comes from the need to avoid cascade back to
 
158
        # parent when parent delete is cascading to child.
 
159
        opts = model._meta
 
160
        if any(link != from_field for link in opts.concrete_model._meta.parents.values()):
 
161
            return False
 
162
        # Foreign keys pointing to this model, both from m2m and other
 
163
        # models.
 
164
        for related in opts.get_all_related_objects(
 
165
            include_hidden=True, include_proxy_eq=True):
 
166
            if related.field.rel.on_delete is not DO_NOTHING:
 
167
                return False
 
168
        # GFK deletes
 
169
        for relation in opts.many_to_many:
 
170
            if not relation.rel.through:
 
171
                return False
 
172
        return True
 
173
 
133
174
    def collect(self, objs, source=None, nullable=False, collect_related=True,
134
175
        source_attr=None, reverse_dependency=False):
135
176
        """
147
188
        models, the one case in which the cascade follows the forwards
148
189
        direction of an FK rather than the reverse direction.)
149
190
        """
 
191
        if self.can_fast_delete(objs):
 
192
            self.fast_deletes.append(objs)
 
193
            return
150
194
        new_objs = self.add(objs, source, nullable,
151
195
                            reverse_dependency=reverse_dependency)
152
196
        if not new_objs:
154
198
 
155
199
        model = new_objs[0].__class__
156
200
 
157
 
        # Recursively collect parent models, but not their related objects.
158
 
        # These will be found by meta.get_all_related_objects()
159
 
        for parent_model, ptr in model._meta.parents.iteritems():
 
201
        # Recursively collect concrete model's parent models, but not their
 
202
        # related objects. These will be found by meta.get_all_related_objects()
 
203
        concrete_model = model._meta.concrete_model
 
204
        for ptr in six.itervalues(concrete_model._meta.parents):
160
205
            if ptr:
 
206
                # FIXME: This seems to be buggy and execute a query for each
 
207
                # parent object fetch. We have the parent data in the obj,
 
208
                # but we don't have a nice way to turn that data into parent
 
209
                # object instance.
161
210
                parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
162
211
                self.collect(parent_objs, source=model,
163
212
                             source_attr=ptr.rel.related_name,
168
217
            for related in model._meta.get_all_related_objects(
169
218
                    include_hidden=True, include_proxy_eq=True):
170
219
                field = related.field
171
 
                if related.model._meta.auto_created:
172
 
                    self.add_batch(related.model, field, new_objs)
173
 
                else:
174
 
                    sub_objs = self.related_objects(related, new_objs)
175
 
                    if not sub_objs:
176
 
                        continue
 
220
                if field.rel.on_delete == DO_NOTHING:
 
221
                    continue
 
222
                sub_objs = self.related_objects(related, new_objs)
 
223
                if self.can_fast_delete(sub_objs, from_field=field):
 
224
                    self.fast_deletes.append(sub_objs)
 
225
                elif sub_objs:
177
226
                    field.rel.on_delete(self, field, sub_objs, self.using)
178
227
 
179
228
            # TODO This entire block is only needed as a special case to
198
247
        )
199
248
 
200
249
    def instances_with_model(self):
201
 
        for model, instances in self.data.iteritems():
 
250
        for model, instances in six.iteritems(self.data):
202
251
            for obj in instances:
203
252
                yield model, obj
204
253
 
205
254
    def sort(self):
206
255
        sorted_models = []
207
256
        concrete_models = set()
208
 
        models = self.data.keys()
 
257
        models = list(self.data)
209
258
        while len(sorted_models) < len(models):
210
259
            found = False
211
260
            for model in models:
239
288
                    sender=model, instance=obj, using=self.using
240
289
                )
241
290
 
 
291
        # fast deletes
 
292
        for qs in self.fast_deletes:
 
293
            qs._raw_delete(using=self.using)
 
294
 
242
295
        # update fields
243
 
        for model, instances_for_fieldvalues in self.field_updates.iteritems():
 
296
        for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
244
297
            query = sql.UpdateQuery(model)
245
 
            for (field, value), instances in instances_for_fieldvalues.iteritems():
 
298
            for (field, value), instances in six.iteritems(instances_for_fieldvalues):
246
299
                query.update_batch([obj.pk for obj in instances],
247
300
                                   {field.name: value}, self.using)
248
301
 
249
302
        # reverse instance collections
250
 
        for instances in self.data.itervalues():
 
303
        for instances in six.itervalues(self.data):
251
304
            instances.reverse()
252
305
 
253
306
        # delete batches
254
 
        for model, batches in self.batches.iteritems():
 
307
        for model, batches in six.iteritems(self.batches):
255
308
            query = sql.DeleteQuery(model)
256
 
            for field, instances in batches.iteritems():
 
309
            for field, instances in six.iteritems(batches):
257
310
                query.delete_batch([obj.pk for obj in instances], self.using, field)
258
311
 
259
312
        # delete instances
260
 
        for model, instances in self.data.iteritems():
 
313
        for model, instances in six.iteritems(self.data):
261
314
            query = sql.DeleteQuery(model)
262
315
            pk_list = [obj.pk for obj in instances]
263
316
            query.delete_batch(pk_list, self.using)
270
323
                )
271
324
 
272
325
        # update collected instances
273
 
        for model, instances_for_fieldvalues in self.field_updates.iteritems():
274
 
            for (field, value), instances in instances_for_fieldvalues.iteritems():
 
326
        for model, instances_for_fieldvalues in six.iteritems(self.field_updates):
 
327
            for (field, value), instances in six.iteritems(instances_for_fieldvalues):
275
328
                for obj in instances:
276
329
                    setattr(obj, field.attname, value)
277
 
        for model, instances in self.data.iteritems():
 
330
        for model, instances in six.iteritems(self.data):
278
331
            for instance in instances:
279
332
                setattr(instance, model._meta.pk.attname, None)