1 import warnings |
1 try: |
2 |
2 set |
3 from django.conf import settings |
3 except NameError: |
|
4 from sets import Set as set # Python 2.3 fallback |
|
5 |
4 from django.db import connection, transaction, IntegrityError |
6 from django.db import connection, transaction, IntegrityError |
5 from django.db.models.fields import DateField, FieldDoesNotExist |
7 from django.db.models.fields import DateField |
6 from django.db.models.query_utils import Q |
8 from django.db.models.query_utils import Q, select_related_descend |
7 from django.db.models import signals, sql |
9 from django.db.models import signals, sql |
8 from django.dispatch import dispatcher |
|
9 from django.utils.datastructures import SortedDict |
10 from django.utils.datastructures import SortedDict |
|
11 |
10 |
12 |
11 # Used to control how many objects are worked with at once in some cases (e.g. |
13 # Used to control how many objects are worked with at once in some cases (e.g. |
12 # when deleting objects). |
14 # when deleting objects). |
13 CHUNK_SIZE = 100 |
15 CHUNK_SIZE = 100 |
14 ITER_CHUNK_SIZE = CHUNK_SIZE |
16 ITER_CHUNK_SIZE = CHUNK_SIZE |
15 |
17 |
16 # Pull into this namespace for backwards compatibility |
18 # The maximum number of items to display in a QuerySet.__repr__ |
|
19 REPR_OUTPUT_SIZE = 20 |
|
20 |
|
21 # Pull into this namespace for backwards compatibility. |
17 EmptyResultSet = sql.EmptyResultSet |
22 EmptyResultSet = sql.EmptyResultSet |
18 |
23 |
|
24 |
|
25 class CyclicDependency(Exception): |
|
26 """ |
|
27 An error when dealing with a collection of objects that have a cyclic |
|
28 dependency, i.e. when deleting multiple objects. |
|
29 """ |
|
30 pass |
|
31 |
|
32 |
|
33 class CollectedObjects(object): |
|
34 """ |
|
35 A container that stores keys and lists of values along with remembering the |
|
36 parent objects for all the keys. |
|
37 |
|
38 This is used for the database object deletion routines so that we can |
|
39 calculate the 'leaf' objects which should be deleted first. |
|
40 """ |
|
41 |
|
42 def __init__(self): |
|
43 self.data = {} |
|
44 self.children = {} |
|
45 |
|
46 def add(self, model, pk, obj, parent_model, nullable=False): |
|
47 """ |
|
48 Adds an item to the container. |
|
49 |
|
50 Arguments: |
|
51 * model - the class of the object being added. |
|
52 * pk - the primary key. |
|
53 * obj - the object itself. |
|
54 * parent_model - the model of the parent object that this object was |
|
55 reached through. |
|
56 * nullable - should be True if this relation is nullable. |
|
57 |
|
58 Returns True if the item already existed in the structure and |
|
59 False otherwise. |
|
60 """ |
|
61 d = self.data.setdefault(model, SortedDict()) |
|
62 retval = pk in d |
|
63 d[pk] = obj |
|
64 # Nullable relationships can be ignored -- they are nulled out before |
|
65 # deleting, and therefore do not affect the order in which objects |
|
66 # have to be deleted. |
|
67 if parent_model is not None and not nullable: |
|
68 self.children.setdefault(parent_model, []).append(model) |
|
69 return retval |
|
70 |
|
71 def __contains__(self, key): |
|
72 return self.data.__contains__(key) |
|
73 |
|
74 def __getitem__(self, key): |
|
75 return self.data[key] |
|
76 |
|
77 def __nonzero__(self): |
|
78 return bool(self.data) |
|
79 |
|
80 def iteritems(self): |
|
81 for k in self.ordered_keys(): |
|
82 yield k, self[k] |
|
83 |
|
84 def items(self): |
|
85 return list(self.iteritems()) |
|
86 |
|
87 def keys(self): |
|
88 return self.ordered_keys() |
|
89 |
|
90 def ordered_keys(self): |
|
91 """ |
|
92 Returns the models in the order that they should be dealt with (i.e. |
|
93 models with no dependencies first). |
|
94 """ |
|
95 dealt_with = SortedDict() |
|
96 # Start with items that have no children |
|
97 models = self.data.keys() |
|
98 while len(dealt_with) < len(models): |
|
99 found = False |
|
100 for model in models: |
|
101 if model in dealt_with: |
|
102 continue |
|
103 children = self.children.setdefault(model, []) |
|
104 if len([c for c in children if c not in dealt_with]) == 0: |
|
105 dealt_with[model] = None |
|
106 found = True |
|
107 if not found: |
|
108 raise CyclicDependency( |
|
109 "There is a cyclic dependency of items to be processed.") |
|
110 |
|
111 return dealt_with.keys() |
|
112 |
|
113 def unordered_keys(self): |
|
114 """ |
|
115 Fallback for the case where is a cyclic dependency but we don't care. |
|
116 """ |
|
117 return self.data.keys() |
|
118 |
|
119 |
19 class QuerySet(object): |
120 class QuerySet(object): |
20 "Represents a lazy database lookup for a set of objects" |
121 """ |
|
122 Represents a lazy database lookup for a set of objects. |
|
123 """ |
21 def __init__(self, model=None, query=None): |
124 def __init__(self, model=None, query=None): |
22 self.model = model |
125 self.model = model |
23 self.query = query or sql.Query(self.model, connection) |
126 self.query = query or sql.Query(self.model, connection) |
24 self._result_cache = None |
127 self._result_cache = None |
25 self._iter = None |
128 self._iter = None |
|
129 self._sticky_filter = False |
26 |
130 |
27 ######################## |
131 ######################## |
28 # PYTHON MAGIC METHODS # |
132 # PYTHON MAGIC METHODS # |
29 ######################## |
133 ######################## |
30 |
134 |
31 def __getstate__(self): |
135 def __getstate__(self): |
32 """ |
136 """ |
33 Allows the Queryset to be pickled. |
137 Allows the QuerySet to be pickled. |
34 """ |
138 """ |
35 # Force the cache to be fully populated. |
139 # Force the cache to be fully populated. |
36 len(self) |
140 len(self) |
37 |
141 |
38 obj_dict = self.__dict__.copy() |
142 obj_dict = self.__dict__.copy() |
39 obj_dict['_iter'] = None |
143 obj_dict['_iter'] = None |
40 return obj_dict |
144 return obj_dict |
41 |
145 |
42 def __repr__(self): |
146 def __repr__(self): |
43 return repr(list(self)) |
147 data = list(self[:REPR_OUTPUT_SIZE + 1]) |
|
148 if len(data) > REPR_OUTPUT_SIZE: |
|
149 data[-1] = "...(remaining elements truncated)..." |
|
150 return repr(data) |
44 |
151 |
45 def __len__(self): |
152 def __len__(self): |
46 # Since __len__ is called quite frequently (for example, as part of |
153 # Since __len__ is called quite frequently (for example, as part of |
47 # list(qs), we make some effort here to be as efficient as possible |
154 # list(qs), we make some effort here to be as efficient as possible |
48 # whilst not messing up any existing iterators against the queryset. |
155 # whilst not messing up any existing iterators against the QuerySet. |
49 if self._result_cache is None: |
156 if self._result_cache is None: |
50 if self._iter: |
157 if self._iter: |
51 self._result_cache = list(self._iter) |
158 self._result_cache = list(self._iter) |
52 else: |
159 else: |
53 self._result_cache = list(self.iterator()) |
160 self._result_cache = list(self.iterator()) |
290 def update(self, **kwargs): |
408 def update(self, **kwargs): |
291 """ |
409 """ |
292 Updates all elements in the current QuerySet, setting all the given |
410 Updates all elements in the current QuerySet, setting all the given |
293 fields to the appropriate values. |
411 fields to the appropriate values. |
294 """ |
412 """ |
|
413 assert self.query.can_filter(), \ |
|
414 "Cannot update a query once a slice has been taken." |
295 query = self.query.clone(sql.UpdateQuery) |
415 query = self.query.clone(sql.UpdateQuery) |
296 query.add_update_values(kwargs) |
416 query.add_update_values(kwargs) |
297 query.execute_sql(None) |
417 rows = query.execute_sql(None) |
298 transaction.commit_unless_managed() |
418 transaction.commit_unless_managed() |
299 self._result_cache = None |
419 self._result_cache = None |
|
420 return rows |
300 update.alters_data = True |
421 update.alters_data = True |
301 |
422 |
302 def _update(self, values): |
423 def _update(self, values): |
303 """ |
424 """ |
304 A version of update that accepts field objects instead of field names. |
425 A version of update that accepts field objects instead of field names. |
305 Used primarily for model saving and not intended for use by general |
426 Used primarily for model saving and not intended for use by general |
306 code (it requires too much poking around at model internals to be |
427 code (it requires too much poking around at model internals to be |
307 useful at that level). |
428 useful at that level). |
308 """ |
429 """ |
|
430 assert self.query.can_filter(), \ |
|
431 "Cannot update a query once a slice has been taken." |
309 query = self.query.clone(sql.UpdateQuery) |
432 query = self.query.clone(sql.UpdateQuery) |
310 query.add_update_fields(values) |
433 query.add_update_fields(values) |
311 query.execute_sql(None) |
|
312 self._result_cache = None |
434 self._result_cache = None |
|
435 return query.execute_sql(None) |
313 _update.alters_data = True |
436 _update.alters_data = True |
314 |
437 |
315 ################################################## |
438 ################################################## |
316 # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # |
439 # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # |
317 ################################################## |
440 ################################################## |
329 return self._clone(klass=ValuesListQuerySet, setup=True, flat=flat, |
452 return self._clone(klass=ValuesListQuerySet, setup=True, flat=flat, |
330 _fields=fields) |
453 _fields=fields) |
331 |
454 |
332 def dates(self, field_name, kind, order='ASC'): |
455 def dates(self, field_name, kind, order='ASC'): |
333 """ |
456 """ |
334 Returns a list of datetime objects representing all available dates |
457 Returns a list of datetime objects representing all available dates for |
335 for the given field_name, scoped to 'kind'. |
458 the given field_name, scoped to 'kind'. |
336 """ |
459 """ |
337 assert kind in ("month", "year", "day"), \ |
460 assert kind in ("month", "year", "day"), \ |
338 "'kind' must be one of 'year', 'month' or 'day'." |
461 "'kind' must be one of 'year', 'month' or 'day'." |
339 assert order in ('ASC', 'DESC'), \ |
462 assert order in ('ASC', 'DESC'), \ |
340 "'order' must be either 'ASC' or 'DESC'." |
463 "'order' must be either 'ASC' or 'DESC'." |
341 # Let the FieldDoesNotExist exception propagate. |
464 return self._clone(klass=DateQuerySet, setup=True, |
342 field = self.model._meta.get_field(field_name, many_to_many=False) |
465 _field_name=field_name, _kind=kind, _order=order) |
343 assert isinstance(field, DateField), "%r isn't a DateField." \ |
|
344 % field_name |
|
345 return self._clone(klass=DateQuerySet, setup=True, _field=field, |
|
346 _kind=kind, _order=order) |
|
347 |
466 |
348 def none(self): |
467 def none(self): |
349 """ |
468 """ |
350 Returns an empty queryset. |
469 Returns an empty QuerySet. |
351 """ |
470 """ |
352 return self._clone(klass=EmptyQuerySet) |
471 return self._clone(klass=EmptyQuerySet) |
353 |
472 |
354 ################################################################## |
473 ################################################################## |
355 # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # |
474 # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # |
389 return clone |
508 return clone |
390 |
509 |
391 def complex_filter(self, filter_obj): |
510 def complex_filter(self, filter_obj): |
392 """ |
511 """ |
393 Returns a new QuerySet instance with filter_obj added to the filters. |
512 Returns a new QuerySet instance with filter_obj added to the filters. |
|
513 |
394 filter_obj can be a Q object (or anything with an add_to_query() |
514 filter_obj can be a Q object (or anything with an add_to_query() |
395 method) or a dictionary of keyword lookup arguments. |
515 method) or a dictionary of keyword lookup arguments. |
396 |
516 |
397 This exists to support framework features such as 'limit_choices_to', |
517 This exists to support framework features such as 'limit_choices_to', |
398 and usually it will be more natural to use other methods. |
518 and usually it will be more natural to use other methods. |
399 """ |
519 """ |
400 if isinstance(filter_obj, Q) or hasattr(filter_obj, 'add_to_query'): |
520 if isinstance(filter_obj, Q) or hasattr(filter_obj, 'add_to_query'): |
401 return self._filter_or_exclude(None, filter_obj) |
521 clone = self._clone() |
|
522 clone.query.add_q(filter_obj) |
|
523 return clone |
402 else: |
524 else: |
403 return self._filter_or_exclude(None, **filter_obj) |
525 return self._filter_or_exclude(None, **filter_obj) |
404 |
526 |
405 def select_related(self, *fields, **kwargs): |
527 def select_related(self, *fields, **kwargs): |
406 """ |
528 """ |
407 Returns a new QuerySet instance that will select related objects. If |
529 Returns a new QuerySet instance that will select related objects. |
408 fields are specified, they must be ForeignKey fields and only those |
530 |
|
531 If fields are specified, they must be ForeignKey fields and only those |
409 related objects are included in the selection. |
532 related objects are included in the selection. |
410 """ |
533 """ |
411 depth = kwargs.pop('depth', 0) |
534 depth = kwargs.pop('depth', 0) |
412 if kwargs: |
535 if kwargs: |
413 raise TypeError('Unexpected keyword arguments to select_related: %s' |
536 raise TypeError('Unexpected keyword arguments to select_related: %s' |
489 for i in range(num or ITER_CHUNK_SIZE): |
617 for i in range(num or ITER_CHUNK_SIZE): |
490 self._result_cache.append(self._iter.next()) |
618 self._result_cache.append(self._iter.next()) |
491 except StopIteration: |
619 except StopIteration: |
492 self._iter = None |
620 self._iter = None |
493 |
621 |
|
622 def _next_is_sticky(self): |
|
623 """ |
|
624 Indicates that the next filter call and the one following that should |
|
625 be treated as a single filter. This is only important when it comes to |
|
626 determining when to reuse tables for many-to-many filters. Required so |
|
627 that we can filter naturally on the results of related managers. |
|
628 |
|
629 This doesn't return a clone of the current QuerySet (it returns |
|
630 "self"). The method is only used internally and should be immediately |
|
631 followed by a filter() that does create a clone. |
|
632 """ |
|
633 self._sticky_filter = True |
|
634 return self |
|
635 |
494 def _merge_sanity_check(self, other): |
636 def _merge_sanity_check(self, other): |
495 """ |
637 """ |
496 Checks that we are merging two comparable queryset classes. |
638 Checks that we are merging two comparable QuerySet classes. By default |
497 """ |
639 this does nothing, but see the ValuesQuerySet for an example of where |
498 if self.__class__ is not other.__class__: |
640 it's useful. |
499 raise TypeError("Cannot merge querysets of different types ('%s' and '%s'." |
641 """ |
500 % (self.__class__.__name__, other.__class__.__name__)) |
642 pass |
|
643 |
501 |
644 |
502 class ValuesQuerySet(QuerySet): |
645 class ValuesQuerySet(QuerySet): |
503 def __init__(self, *args, **kwargs): |
646 def __init__(self, *args, **kwargs): |
504 super(ValuesQuerySet, self).__init__(*args, **kwargs) |
647 super(ValuesQuerySet, self).__init__(*args, **kwargs) |
505 # select_related isn't supported in values(). (FIXME -#3358) |
648 # select_related isn't supported in values(). (FIXME -#3358) |
581 def _clone(self, *args, **kwargs): |
727 def _clone(self, *args, **kwargs): |
582 clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs) |
728 clone = super(ValuesListQuerySet, self)._clone(*args, **kwargs) |
583 clone.flat = self.flat |
729 clone.flat = self.flat |
584 return clone |
730 return clone |
585 |
731 |
|
732 |
586 class DateQuerySet(QuerySet): |
733 class DateQuerySet(QuerySet): |
587 def iterator(self): |
734 def iterator(self): |
588 return self.query.results_iter() |
735 return self.query.results_iter() |
589 |
736 |
590 def _setup_query(self): |
737 def _setup_query(self): |
591 """ |
738 """ |
592 Sets up any special features of the query attribute. |
739 Sets up any special features of the query attribute. |
593 |
740 |
594 Called by the _clone() method after initialising the rest of the |
741 Called by the _clone() method after initializing the rest of the |
595 instance. |
742 instance. |
596 """ |
743 """ |
597 self.query = self.query.clone(klass=sql.DateQuery, setup=True) |
744 self.query = self.query.clone(klass=sql.DateQuery, setup=True) |
598 self.query.select = [] |
745 self.query.select = [] |
599 self.query.add_date_select(self._field.column, self._kind, self._order) |
746 field = self.model._meta.get_field(self._field_name, many_to_many=False) |
600 if self._field.null: |
747 assert isinstance(field, DateField), "%r isn't a DateField." \ |
601 self.query.add_filter(('%s__isnull' % self._field.name, True)) |
748 % field.name |
|
749 self.query.add_date_select(field, self._kind, self._order) |
|
750 if field.null: |
|
751 self.query.add_filter(('%s__isnull' % field.name, False)) |
602 |
752 |
603 def _clone(self, klass=None, setup=False, **kwargs): |
753 def _clone(self, klass=None, setup=False, **kwargs): |
604 c = super(DateQuerySet, self)._clone(klass, False, **kwargs) |
754 c = super(DateQuerySet, self)._clone(klass, False, **kwargs) |
605 c._field = self._field |
755 c._field_name = self._field_name |
606 c._kind = self._kind |
756 c._kind = self._kind |
607 if setup and hasattr(c, '_setup_query'): |
757 if setup and hasattr(c, '_setup_query'): |
608 c._setup_query() |
758 c._setup_query() |
609 return c |
759 return c |
610 |
760 |
|
761 |
611 class EmptyQuerySet(QuerySet): |
762 class EmptyQuerySet(QuerySet): |
612 def __init__(self, model=None, query=None): |
763 def __init__(self, model=None, query=None): |
613 super(EmptyQuerySet, self).__init__(model, query) |
764 super(EmptyQuerySet, self).__init__(model, query) |
614 self._result_cache = [] |
765 self._result_cache = [] |
615 |
766 |
|
767 def __and__(self, other): |
|
768 return self._clone() |
|
769 |
|
770 def __or__(self, other): |
|
771 return other._clone() |
|
772 |
616 def count(self): |
773 def count(self): |
617 return 0 |
774 return 0 |
618 |
775 |
619 def delete(self): |
776 def delete(self): |
620 pass |
777 pass |
627 def iterator(self): |
784 def iterator(self): |
628 # This slightly odd construction is because we need an empty generator |
785 # This slightly odd construction is because we need an empty generator |
629 # (it raises StopIteration immediately). |
786 # (it raises StopIteration immediately). |
630 yield iter([]).next() |
787 yield iter([]).next() |
631 |
788 |
632 # QOperator, QNot, QAnd and QOr are temporarily retained for backwards |
|
633 # compatibility. All the old functionality is now part of the 'Q' class. |
|
634 class QOperator(Q): |
|
635 def __init__(self, *args, **kwargs): |
|
636 warnings.warn('Use Q instead of QOr, QAnd or QOperation.', |
|
637 DeprecationWarning, stacklevel=2) |
|
638 super(QOperator, self).__init__(*args, **kwargs) |
|
639 |
|
640 QOr = QAnd = QOperator |
|
641 |
|
642 def QNot(q): |
|
643 warnings.warn('Use ~q instead of QNot(q)', DeprecationWarning, stacklevel=2) |
|
644 return ~q |
|
645 |
789 |
646 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, |
790 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, |
647 requested=None): |
791 requested=None): |
648 """ |
792 """ |
649 Helper function that recursively returns an object with the specified |
793 Helper function that recursively returns an object with the specified |
650 related attributes already populated. |
794 related attributes already populated. |
651 """ |
795 """ |
652 if max_depth and requested is None and cur_depth > max_depth: |
796 if max_depth and requested is None and cur_depth > max_depth: |
653 # We've recursed deeply enough; stop now. |
797 # We've recursed deeply enough; stop now. |
654 return None |
798 return None |
655 |
799 |
656 restricted = requested is not None |
800 restricted = requested is not None |
657 index_end = index_start + len(klass._meta.fields) |
801 index_end = index_start + len(klass._meta.fields) |
658 obj = klass(*row[index_start:index_end]) |
802 fields = row[index_start:index_end] |
|
803 if not [x for x in fields if x is not None]: |
|
804 # If we only have a list of Nones, there was not related object. |
|
805 obj = None |
|
806 else: |
|
807 obj = klass(*fields) |
659 for f in klass._meta.fields: |
808 for f in klass._meta.fields: |
660 if (not f.rel or (not restricted and f.null) or |
809 if not select_related_descend(f, restricted, requested): |
661 (restricted and f.name not in requested) or f.rel.parent_link): |
|
662 continue |
810 continue |
663 if restricted: |
811 if restricted: |
664 next = requested[f.name] |
812 next = requested[f.name] |
665 else: |
813 else: |
666 next = None |
814 next = None |
667 cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, |
815 cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, |
668 cur_depth+1, next) |
816 cur_depth+1, next) |
669 if cached_row: |
817 if cached_row: |
670 rel_obj, index_end = cached_row |
818 rel_obj, index_end = cached_row |
671 setattr(obj, f.get_cache_name(), rel_obj) |
819 if obj is not None: |
|
820 setattr(obj, f.get_cache_name(), rel_obj) |
672 return obj, index_end |
821 return obj, index_end |
|
822 |
673 |
823 |
674 def delete_objects(seen_objs): |
824 def delete_objects(seen_objs): |
675 """ |
825 """ |
676 Iterate through a list of seen classes, and remove any instances that are |
826 Iterate through a list of seen classes, and remove any instances that are |
677 referred to. |
827 referred to. |
678 """ |
828 """ |
679 ordered_classes = seen_objs.keys() |
829 try: |
680 ordered_classes.reverse() |
830 ordered_classes = seen_objs.keys() |
681 |
831 except CyclicDependency: |
|
832 # If there is a cyclic dependency, we cannot in general delete the |
|
833 # objects. However, if an appropriate transaction is set up, or if the |
|
834 # database is lax enough, it will succeed. So for now, we go ahead and |
|
835 # try anyway. |
|
836 ordered_classes = seen_objs.unordered_keys() |
|
837 |
|
838 obj_pairs = {} |
682 for cls in ordered_classes: |
839 for cls in ordered_classes: |
683 seen_objs[cls] = seen_objs[cls].items() |
840 items = seen_objs[cls].items() |
684 seen_objs[cls].sort() |
841 items.sort() |
685 |
842 obj_pairs[cls] = items |
686 # Pre notify all instances to be deleted |
843 |
687 for pk_val, instance in seen_objs[cls]: |
844 # Pre-notify all instances to be deleted. |
688 dispatcher.send(signal=signals.pre_delete, sender=cls, |
845 for pk_val, instance in items: |
689 instance=instance) |
846 signals.pre_delete.send(sender=cls, instance=instance) |
690 |
847 |
691 pk_list = [pk for pk,instance in seen_objs[cls]] |
848 pk_list = [pk for pk,instance in items] |
692 del_query = sql.DeleteQuery(cls, connection) |
849 del_query = sql.DeleteQuery(cls, connection) |
693 del_query.delete_batch_related(pk_list) |
850 del_query.delete_batch_related(pk_list) |
694 |
851 |
695 update_query = sql.UpdateQuery(cls, connection) |
852 update_query = sql.UpdateQuery(cls, connection) |
696 for field in cls._meta.fields: |
853 for field, model in cls._meta.get_fields_with_model(): |
697 if field.rel and field.null and field.rel.to in seen_objs: |
854 if (field.rel and field.null and field.rel.to in seen_objs and |
698 update_query.clear_related(field, pk_list) |
855 filter(lambda f: f.column == field.column, |
699 |
856 field.rel.to._meta.fields)): |
700 # Now delete the actual data |
857 if model: |
|
858 sql.UpdateQuery(model, connection).clear_related(field, |
|
859 pk_list) |
|
860 else: |
|
861 update_query.clear_related(field, pk_list) |
|
862 |
|
863 # Now delete the actual data. |
701 for cls in ordered_classes: |
864 for cls in ordered_classes: |
702 seen_objs[cls].reverse() |
865 items = obj_pairs[cls] |
703 pk_list = [pk for pk,instance in seen_objs[cls]] |
866 items.reverse() |
|
867 |
|
868 pk_list = [pk for pk,instance in items] |
704 del_query = sql.DeleteQuery(cls, connection) |
869 del_query = sql.DeleteQuery(cls, connection) |
705 del_query.delete_batch(pk_list) |
870 del_query.delete_batch(pk_list) |
706 |
871 |
707 # Last cleanup; set NULLs where there once was a reference to the |
872 # Last cleanup; set NULLs where there once was a reference to the |
708 # object, NULL the primary key of the found objects, and perform |
873 # object, NULL the primary key of the found objects, and perform |
709 # post-notification. |
874 # post-notification. |
710 for pk_val, instance in seen_objs[cls]: |
875 for pk_val, instance in items: |
711 for field in cls._meta.fields: |
876 for field in cls._meta.fields: |
712 if field.rel and field.null and field.rel.to in seen_objs: |
877 if field.rel and field.null and field.rel.to in seen_objs: |
713 setattr(instance, field.attname, None) |
878 setattr(instance, field.attname, None) |
714 |
879 |
715 dispatcher.send(signal=signals.post_delete, sender=cls, |
880 signals.post_delete.send(sender=cls, instance=instance) |
716 instance=instance) |
|
717 setattr(instance, cls._meta.pk.attname, None) |
881 setattr(instance, cls._meta.pk.attname, None) |
718 |
882 |
719 transaction.commit_unless_managed() |
883 transaction.commit_unless_managed() |
|
884 |
720 |
885 |
721 def insert_query(model, values, return_id=False, raw_values=False): |
886 def insert_query(model, values, return_id=False, raw_values=False): |
722 """ |
887 """ |
723 Inserts a new record for the given model. This provides an interface to |
888 Inserts a new record for the given model. This provides an interface to |
724 the InsertQuery class and is how Model.save() is implemented. It is not |
889 the InsertQuery class and is how Model.save() is implemented. It is not |
725 part of the public API. |
890 part of the public API. |
726 """ |
891 """ |
727 query = sql.InsertQuery(model, connection) |
892 query = sql.InsertQuery(model, connection) |
728 query.insert_values(values, raw_values) |
893 query.insert_values(values, raw_values) |
729 return query.execute_sql(return_id) |
894 return query.execute_sql(return_id) |
730 |
|