Commit 81c2d9f6 authored by Andriy Sokolovskiy's avatar Andriy Sokolovskiy Committed by Tim Graham
Browse files

Fixed #15579 -- Added ability to delete only child models in multi-table inheritance.

parent b9cb8157
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -827,7 +827,7 @@ class Model(six.with_metaclass(ModelBase)):
        return manager._insert([self], fields=fields, return_id=update_pk,
                               using=using, raw=raw)

    def delete(self, using=None):
    def delete(self, using=None, keep_parents=False):
        using = using or router.db_for_write(self.__class__, instance=self)
        assert self._get_pk_val() is not None, (
            "%s object can't be deleted because its %s attribute is set to None." %
@@ -835,7 +835,7 @@ class Model(six.with_metaclass(ModelBase)):
        )

        collector = Collector(using=using)
        collector.collect([self])
        collector.collect([self], keep_parents=keep_parents)
        collector.delete()

    delete.alters_data = True
+19 −15
Original line number Diff line number Diff line
@@ -174,7 +174,7 @@ class Collector(object):
            return [objs]

    def collect(self, objs, source=None, nullable=False, collect_related=True,
            source_attr=None, reverse_dependency=False):
            source_attr=None, reverse_dependency=False, keep_parents=False):
        """
        Adds 'objs' to the collection of objects to be deleted as well as all
        parent instances.  'objs' must be a homogeneous iterable collection of
@@ -189,6 +189,9 @@ class Collector(object):
        current model, rather than after. (Needed for cascading to parent
        models, the one case in which the cascade follows the forwards
        direction of an FK rather than the reverse direction.)

        If 'keep_parents' is False, data of parent's models will be not
        deleted.
        """
        if self.can_fast_delete(objs):
            self.fast_deletes.append(objs)
@@ -200,6 +203,7 @@ class Collector(object):

        model = new_objs[0].__class__

        if not keep_parents:
            # Recursively collect concrete model's parent models, but not their
            # related objects. These will be found by meta.get_fields()
            concrete_model = model._meta.concrete_model
+9 −1
Original line number Diff line number Diff line
@@ -533,7 +533,7 @@ value, the field will be added to the updated fields.
Deleting objects
================

.. method:: Model.delete([using=DEFAULT_DB_ALIAS])
.. method:: Model.delete([using=DEFAULT_DB_ALIAS, keep_parents=False])

Issues an SQL ``DELETE`` for the object. This only deletes the object in the
database; the Python instance will still exist and will still have data in
@@ -545,6 +545,14 @@ For more details, including how to delete objects in bulk, see
If you want customized deletion behavior, you can override the ``delete()``
method. See :ref:`overriding-model-methods` for more details.

Sometimes with :ref:`multi-table inheritance <multi-table-inheritance>` you may
want to delete only a child model's data. Specifying ``keep_parents=True`` will
keep the parent model's data.

.. versionchanged:: 1.9

    The ``keep_parents`` parameter was added.

Pickling objects
================

+4 −0
Original line number Diff line number Diff line
@@ -151,6 +151,10 @@ Models
  managers created by ``ForeignKey``, ``GenericForeignKey``, and
  ``ManyToManyField``.

* Added the ``keep_parents`` parameter to :meth:`Model.delete()
  <django.db.models.Model.delete>` to allow deleting only a child's data in a
  model that uses multi-table inheritance.

CSRF
^^^^

+7 −0
Original line number Diff line number Diff line
@@ -349,6 +349,13 @@ class DeletionTests(TestCase):
        self.assertFalse(S.objects.exists())
        self.assertFalse(T.objects.exists())

    def test_delete_with_keeping_parents(self):
        child = RChild.objects.create()
        parent_id = child.r_ptr_id
        child.delete(keep_parents=True)
        self.assertFalse(RChild.objects.filter(id=child.id).exists())
        self.assertTrue(R.objects.filter(id=parent_id).exists())


class FastDeleteTests(TestCase):