mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
Fixed #15579 -- Added ability to delete only child models in multi-table inheritance.
This commit is contained in:
parent
b9cb81570e
commit
81c2d9f60b
@ -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
|
||||
|
@ -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,20 +203,21 @@ class Collector(object):
|
||||
|
||||
model = new_objs[0].__class__
|
||||
|
||||
# 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
|
||||
for ptr in six.itervalues(concrete_model._meta.parents):
|
||||
if ptr:
|
||||
# FIXME: This seems to be buggy and execute a query for each
|
||||
# parent object fetch. We have the parent data in the obj,
|
||||
# but we don't have a nice way to turn that data into parent
|
||||
# object instance.
|
||||
parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
|
||||
self.collect(parent_objs, source=model,
|
||||
source_attr=ptr.rel.related_name,
|
||||
collect_related=False,
|
||||
reverse_dependency=True)
|
||||
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
|
||||
for ptr in six.itervalues(concrete_model._meta.parents):
|
||||
if ptr:
|
||||
# FIXME: This seems to be buggy and execute a query for each
|
||||
# parent object fetch. We have the parent data in the obj,
|
||||
# but we don't have a nice way to turn that data into parent
|
||||
# object instance.
|
||||
parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
|
||||
self.collect(parent_objs, source=model,
|
||||
source_attr=ptr.rel.related_name,
|
||||
collect_related=False,
|
||||
reverse_dependency=True)
|
||||
|
||||
if collect_related:
|
||||
for related in get_candidate_relations_to_delete(model._meta):
|
||||
|
@ -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
|
||||
================
|
||||
|
||||
|
@ -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
|
||||
^^^^
|
||||
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user