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

This commit is contained in:
Andriy Sokolovskiy 2015-02-07 01:16:26 +02:00 committed by Tim Graham
parent b9cb81570e
commit 81c2d9f60b
5 changed files with 41 additions and 18 deletions

View File

@ -827,7 +827,7 @@ class Model(six.with_metaclass(ModelBase)):
return manager._insert([self], fields=fields, return_id=update_pk, return manager._insert([self], fields=fields, return_id=update_pk,
using=using, raw=raw) 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) using = using or router.db_for_write(self.__class__, instance=self)
assert self._get_pk_val() is not None, ( assert self._get_pk_val() is not None, (
"%s object can't be deleted because its %s attribute is set to 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 = Collector(using=using)
collector.collect([self]) collector.collect([self], keep_parents=keep_parents)
collector.delete() collector.delete()
delete.alters_data = True delete.alters_data = True

View File

@ -174,7 +174,7 @@ class Collector(object):
return [objs] return [objs]
def collect(self, objs, source=None, nullable=False, collect_related=True, 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 Adds 'objs' to the collection of objects to be deleted as well as all
parent instances. 'objs' must be a homogeneous iterable collection of 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 current model, rather than after. (Needed for cascading to parent
models, the one case in which the cascade follows the forwards models, the one case in which the cascade follows the forwards
direction of an FK rather than the reverse direction.) 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): if self.can_fast_delete(objs):
self.fast_deletes.append(objs) self.fast_deletes.append(objs)
@ -200,6 +203,7 @@ class Collector(object):
model = new_objs[0].__class__ model = new_objs[0].__class__
if not keep_parents:
# Recursively collect concrete model's parent models, but not their # Recursively collect concrete model's parent models, but not their
# related objects. These will be found by meta.get_fields() # related objects. These will be found by meta.get_fields()
concrete_model = model._meta.concrete_model concrete_model = model._meta.concrete_model

View File

@ -533,7 +533,7 @@ value, the field will be added to the updated fields.
Deleting objects 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 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 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()`` If you want customized deletion behavior, you can override the ``delete()``
method. See :ref:`overriding-model-methods` for more details. 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 Pickling objects
================ ================

View File

@ -151,6 +151,10 @@ Models
managers created by ``ForeignKey``, ``GenericForeignKey``, and managers created by ``ForeignKey``, ``GenericForeignKey``, and
``ManyToManyField``. ``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 CSRF
^^^^ ^^^^

View File

@ -349,6 +349,13 @@ class DeletionTests(TestCase):
self.assertFalse(S.objects.exists()) self.assertFalse(S.objects.exists())
self.assertFalse(T.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): class FastDeleteTests(TestCase):