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,
|
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
|
||||||
|
@ -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,20 +203,21 @@ class Collector(object):
|
|||||||
|
|
||||||
model = new_objs[0].__class__
|
model = new_objs[0].__class__
|
||||||
|
|
||||||
# Recursively collect concrete model's parent models, but not their
|
if not keep_parents:
|
||||||
# related objects. These will be found by meta.get_fields()
|
# Recursively collect concrete model's parent models, but not their
|
||||||
concrete_model = model._meta.concrete_model
|
# related objects. These will be found by meta.get_fields()
|
||||||
for ptr in six.itervalues(concrete_model._meta.parents):
|
concrete_model = model._meta.concrete_model
|
||||||
if ptr:
|
for ptr in six.itervalues(concrete_model._meta.parents):
|
||||||
# FIXME: This seems to be buggy and execute a query for each
|
if ptr:
|
||||||
# parent object fetch. We have the parent data in the obj,
|
# FIXME: This seems to be buggy and execute a query for each
|
||||||
# but we don't have a nice way to turn that data into parent
|
# parent object fetch. We have the parent data in the obj,
|
||||||
# object instance.
|
# but we don't have a nice way to turn that data into parent
|
||||||
parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
|
# object instance.
|
||||||
self.collect(parent_objs, source=model,
|
parent_objs = [getattr(obj, ptr.name) for obj in new_objs]
|
||||||
source_attr=ptr.rel.related_name,
|
self.collect(parent_objs, source=model,
|
||||||
collect_related=False,
|
source_attr=ptr.rel.related_name,
|
||||||
reverse_dependency=True)
|
collect_related=False,
|
||||||
|
reverse_dependency=True)
|
||||||
|
|
||||||
if collect_related:
|
if collect_related:
|
||||||
for related in get_candidate_relations_to_delete(model._meta):
|
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
|
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
|
||||||
================
|
================
|
||||||
|
|
||||||
|
@ -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
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user