Loading django/contrib/contenttypes/generic.py +20 −7 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ from functools import partial from django.core.exceptions import ObjectDoesNotExist from django.db import connection from django.db import models, router, DEFAULT_DB_ALIAS from django.db import models, router, transaction, DEFAULT_DB_ALIAS from django.db.models import signals from django.db.models.fields.related import ForeignObject, ForeignObjectRel from django.db.models.related import PathInfo Loading Loading @@ -382,16 +382,29 @@ def create_generic_related_manager(superclass): obj.save() add.alters_data = True def remove(self, *objs): db = router.db_for_write(self.model, instance=self.instance) self.using(db).filter(pk__in=[o.pk for o in objs]).delete() def remove(self, *objs, **kwargs): if not objs: return bulk = kwargs.pop('bulk', True) self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk) remove.alters_data = True def clear(self): db = router.db_for_write(self.model, instance=self.instance) self.using(db).delete() def clear(self, **kwargs): bulk = kwargs.pop('bulk', True) self._clear(self, bulk) clear.alters_data = True def _clear(self, queryset, bulk): db = router.db_for_write(self.model, instance=self.instance) queryset = queryset.using(db) if bulk: queryset.delete() else: with transaction.commit_on_success_unless_managed(using=db, savepoint=False): for obj in queryset: obj.delete() _clear.alters_data = True def create(self, **kwargs): kwargs[self.content_type_field_name] = self.content_type kwargs[self.object_id_field_name] = self.pk_val Loading django/db/models/fields/related.py +19 −10 Original line number Diff line number Diff line Loading @@ -463,13 +463,11 @@ def create_foreign_related_manager(superclass, rel_field, rel_model): # remove() and clear() are only provided if the ForeignKey can have a value of null. if rel_field.null: def remove(self, *objs): # If there aren't any objects, there is nothing to do. def remove(self, *objs, **kwargs): if not objs: return bulk = kwargs.pop('bulk', True) val = rel_field.get_foreign_related_value(self.instance) old_ids = set() for obj in objs: # Is obj actually part of this descriptor set? Loading @@ -477,14 +475,26 @@ def create_foreign_related_manager(superclass, rel_field, rel_model): old_ids.add(obj.pk) else: raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance)) self.filter(pk__in=old_ids).update(**{rel_field.name: None}) self._clear(self.filter(pk__in=old_ids), bulk) remove.alters_data = True def clear(self): self.update(**{rel_field.name: None}) def clear(self, **kwargs): bulk = kwargs.pop('bulk', True) self._clear(self, bulk) clear.alters_data = True def _clear(self, queryset, bulk): db = router.db_for_write(self.model, instance=self.instance) queryset = queryset.using(db) if bulk: queryset.update(**{rel_field.name: None}) else: with transaction.commit_on_success_unless_managed(using=db, savepoint=False): for obj in queryset: setattr(obj, rel_field.name, None) obj.save() _clear.alters_data = True return RelatedManager Loading Loading @@ -657,6 +667,7 @@ def create_many_related_manager(superclass, rel): signals.m2m_changed.send(sender=self.through, action="pre_clear", instance=self.instance, reverse=self.reverse, model=self.model, pk_set=None, using=db) filters = self._build_remove_filters(super(ManyRelatedManager, self).get_queryset().using(db)) self.through._default_manager.using(db).filter(filters).delete() Loading Loading @@ -746,8 +757,6 @@ def create_many_related_manager(superclass, rel): # source_field_name: the PK colname in join table for the source object # target_field_name: the PK colname in join table for the target object # *objs - objects to remove # If there aren't any objects, there is nothing to do. if not objs: return Loading django/db/models/query.py +1 −1 Original line number Diff line number Diff line Loading @@ -1030,7 +1030,7 @@ class QuerySet(object): """ Checks if this QuerySet has any filtering going on. Note that this isn't equivalent for checking if all objects are present in results, for example qs[1:]._has_filters() -> True. for example qs[1:]._has_filters() -> False. """ return self.query.has_filters() Loading docs/ref/models/relations.txt +13 −1 Original line number Diff line number Diff line Loading @@ -110,6 +110,17 @@ Related objects reference the ``blog`` :class:`~django.db.models.ForeignKey` doesn't have ``null=True``, this is invalid. .. versionchanged 1.7:: For :class:`~django.db.models.ForeignKey` objects, this method accepts a ``bulk`` argument to control how to perform the operation. If ``True`` (the default), ``QuerySet.update()`` is used. If ``bulk=False``, the ``save()`` method of each individual model instance is called instead. This triggers the :data:`~django.db.models.signals.pre_save` and :data:`~django.db.models.signals.post_save` signals and comes at the expense of performance. .. method:: clear() Removes all objects from the related object set:: Loading @@ -121,7 +132,8 @@ Related objects reference them. Just like ``remove()``, ``clear()`` is only available on :class:`~django.db.models.ForeignKey`\s where ``null=True``. :class:`~django.db.models.ForeignKey`\s where ``null=True`` and it also accepts the ``bulk`` keyword argument. .. note:: Loading docs/releases/1.7.txt +9 −2 Original line number Diff line number Diff line Loading @@ -430,6 +430,11 @@ Models * :class:`F expressions <django.db.models.F>` support the power operator (``**``). * The ``remove()`` and ``clear()`` methods of the related managers created by ``ForeignKey`` and ``GenericForeignKey`` now accept the ``bulk`` keyword argument to control whether or not to perform operations in bulk (i.e. using ``QuerySet.update()``). Defaults to ``True``. Signals ^^^^^^^ Loading Loading @@ -589,11 +594,13 @@ Fixing the issues introduced some backward incompatible changes: - The default implementation of ``remove()`` for ``ForeignKey`` related managers changed from a series of ``Model.save()`` calls to a single ``QuerySet.update()`` call. The change means that ``pre_save`` and ``post_save`` signals aren't sent anymore. ``post_save`` signals aren't sent anymore. You can use the ``bulk=False`` keyword argument to revert to the previous behavior. - The ``remove()`` and ``clear()`` methods for ``GenericForeignKey`` related managers now perform bulk delete. The ``Model.delete()`` method isn't called on each instance anymore. on each instance anymore. You can use the ``bulk=False`` keyword argument to revert to the previous behavior. - The ``remove()`` and ``clear()`` methods for ``ManyToManyField`` related managers perform nested queries when filtering is involved, which may or Loading Loading
django/contrib/contenttypes/generic.py +20 −7 Original line number Diff line number Diff line Loading @@ -8,7 +8,7 @@ from functools import partial from django.core.exceptions import ObjectDoesNotExist from django.db import connection from django.db import models, router, DEFAULT_DB_ALIAS from django.db import models, router, transaction, DEFAULT_DB_ALIAS from django.db.models import signals from django.db.models.fields.related import ForeignObject, ForeignObjectRel from django.db.models.related import PathInfo Loading Loading @@ -382,16 +382,29 @@ def create_generic_related_manager(superclass): obj.save() add.alters_data = True def remove(self, *objs): db = router.db_for_write(self.model, instance=self.instance) self.using(db).filter(pk__in=[o.pk for o in objs]).delete() def remove(self, *objs, **kwargs): if not objs: return bulk = kwargs.pop('bulk', True) self._clear(self.filter(pk__in=[o.pk for o in objs]), bulk) remove.alters_data = True def clear(self): db = router.db_for_write(self.model, instance=self.instance) self.using(db).delete() def clear(self, **kwargs): bulk = kwargs.pop('bulk', True) self._clear(self, bulk) clear.alters_data = True def _clear(self, queryset, bulk): db = router.db_for_write(self.model, instance=self.instance) queryset = queryset.using(db) if bulk: queryset.delete() else: with transaction.commit_on_success_unless_managed(using=db, savepoint=False): for obj in queryset: obj.delete() _clear.alters_data = True def create(self, **kwargs): kwargs[self.content_type_field_name] = self.content_type kwargs[self.object_id_field_name] = self.pk_val Loading
django/db/models/fields/related.py +19 −10 Original line number Diff line number Diff line Loading @@ -463,13 +463,11 @@ def create_foreign_related_manager(superclass, rel_field, rel_model): # remove() and clear() are only provided if the ForeignKey can have a value of null. if rel_field.null: def remove(self, *objs): # If there aren't any objects, there is nothing to do. def remove(self, *objs, **kwargs): if not objs: return bulk = kwargs.pop('bulk', True) val = rel_field.get_foreign_related_value(self.instance) old_ids = set() for obj in objs: # Is obj actually part of this descriptor set? Loading @@ -477,14 +475,26 @@ def create_foreign_related_manager(superclass, rel_field, rel_model): old_ids.add(obj.pk) else: raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance)) self.filter(pk__in=old_ids).update(**{rel_field.name: None}) self._clear(self.filter(pk__in=old_ids), bulk) remove.alters_data = True def clear(self): self.update(**{rel_field.name: None}) def clear(self, **kwargs): bulk = kwargs.pop('bulk', True) self._clear(self, bulk) clear.alters_data = True def _clear(self, queryset, bulk): db = router.db_for_write(self.model, instance=self.instance) queryset = queryset.using(db) if bulk: queryset.update(**{rel_field.name: None}) else: with transaction.commit_on_success_unless_managed(using=db, savepoint=False): for obj in queryset: setattr(obj, rel_field.name, None) obj.save() _clear.alters_data = True return RelatedManager Loading Loading @@ -657,6 +667,7 @@ def create_many_related_manager(superclass, rel): signals.m2m_changed.send(sender=self.through, action="pre_clear", instance=self.instance, reverse=self.reverse, model=self.model, pk_set=None, using=db) filters = self._build_remove_filters(super(ManyRelatedManager, self).get_queryset().using(db)) self.through._default_manager.using(db).filter(filters).delete() Loading Loading @@ -746,8 +757,6 @@ def create_many_related_manager(superclass, rel): # source_field_name: the PK colname in join table for the source object # target_field_name: the PK colname in join table for the target object # *objs - objects to remove # If there aren't any objects, there is nothing to do. if not objs: return Loading
django/db/models/query.py +1 −1 Original line number Diff line number Diff line Loading @@ -1030,7 +1030,7 @@ class QuerySet(object): """ Checks if this QuerySet has any filtering going on. Note that this isn't equivalent for checking if all objects are present in results, for example qs[1:]._has_filters() -> True. for example qs[1:]._has_filters() -> False. """ return self.query.has_filters() Loading
docs/ref/models/relations.txt +13 −1 Original line number Diff line number Diff line Loading @@ -110,6 +110,17 @@ Related objects reference the ``blog`` :class:`~django.db.models.ForeignKey` doesn't have ``null=True``, this is invalid. .. versionchanged 1.7:: For :class:`~django.db.models.ForeignKey` objects, this method accepts a ``bulk`` argument to control how to perform the operation. If ``True`` (the default), ``QuerySet.update()`` is used. If ``bulk=False``, the ``save()`` method of each individual model instance is called instead. This triggers the :data:`~django.db.models.signals.pre_save` and :data:`~django.db.models.signals.post_save` signals and comes at the expense of performance. .. method:: clear() Removes all objects from the related object set:: Loading @@ -121,7 +132,8 @@ Related objects reference them. Just like ``remove()``, ``clear()`` is only available on :class:`~django.db.models.ForeignKey`\s where ``null=True``. :class:`~django.db.models.ForeignKey`\s where ``null=True`` and it also accepts the ``bulk`` keyword argument. .. note:: Loading
docs/releases/1.7.txt +9 −2 Original line number Diff line number Diff line Loading @@ -430,6 +430,11 @@ Models * :class:`F expressions <django.db.models.F>` support the power operator (``**``). * The ``remove()`` and ``clear()`` methods of the related managers created by ``ForeignKey`` and ``GenericForeignKey`` now accept the ``bulk`` keyword argument to control whether or not to perform operations in bulk (i.e. using ``QuerySet.update()``). Defaults to ``True``. Signals ^^^^^^^ Loading Loading @@ -589,11 +594,13 @@ Fixing the issues introduced some backward incompatible changes: - The default implementation of ``remove()`` for ``ForeignKey`` related managers changed from a series of ``Model.save()`` calls to a single ``QuerySet.update()`` call. The change means that ``pre_save`` and ``post_save`` signals aren't sent anymore. ``post_save`` signals aren't sent anymore. You can use the ``bulk=False`` keyword argument to revert to the previous behavior. - The ``remove()`` and ``clear()`` methods for ``GenericForeignKey`` related managers now perform bulk delete. The ``Model.delete()`` method isn't called on each instance anymore. on each instance anymore. You can use the ``bulk=False`` keyword argument to revert to the previous behavior. - The ``remove()`` and ``clear()`` methods for ``ManyToManyField`` related managers perform nested queries when filtering is involved, which may or Loading