在django-mptt树结构中删除子节点的正确方法是什么

时间:2018-09-25 11:51:52

标签: django delete-operator django-mptt mptt

我正在使用Django:

>>> django.VERSION
(1, 11, 15, u'final', 0)

MPTT:

 django-mptt     0.9.1

在型号中:

from mptt.models import MPTTModel, TreeForeignKey
class Location(MPTTModel):
    id              = models.AutoField(primary_key=True)
    name            = models.CharField(max_length=75,null=False)
    parent          = TreeForeignKey('self', on_delete=models.PROTECT, null=True, blank=True)

我可以按照django-mptt文档正确查看和添加。但是我无法删除子节点。它弄乱了所有的树形结构。 这是在视图中使用删除的方式:

Location.objects.get(id=loc_id).delete()

其中loc_id是我要删除的节点的ID。 我不确定如何正确使用Delete或mptt中存在错误。 我在其官方文档中查找了任何示例。该主题仅说明以下内容:

  

mptt.models.MPTTModel类(* args,** kwargs)

     

MPTTModel.delete(* args,** kwargs)
  在节点上调用delete将删除它以及它的完整子树,而不是重新附加所有   子节点到其父节点。没有特定于   MPTT模型,所有参数都将直接传递给Django的   删除模型。

     

删除将不返回任何内容。

谢谢

2 个答案:

答案 0 :(得分:0)

好吧,事实证明,暂时只是为了获得我可以使用的健全表结构

Location.objects.rebuild()

这将重建整个表结构,即其lft,rght列。所以我把树弄糟了。删除节点后,我使用此行。

这可能不是完美的解决方案。

我仍在寻找适当的删除代码,因为为大型数据集重建树可能很耗时。

希望有人在这方面仍然可以提供帮助。

答案 1 :(得分:0)

有一次我遇到了受保护儿童删除的问题(django-mptt==0.9.1)。 经过一番搜索,我发现了下一个问题: 问题发生在 MPTTodel 的 delete() 方法中。 首先它尝试更新树结构,然后删除对象。所以如果我们有受保护的关系,我们的树可能会搞砸。

tree_width = (self._mpttfield('right') -                                
              self._mpttfield('left') + 1)                              
target_right = self._mpttfield('right')                                 
tree_id = self._mpttfield('tree_id')                                    
self._tree_manager._close_gap(tree_width, target_right, tree_id)        
parent = cached_field_value(self, self._mptt_meta.parent_attr)          
if parent:                                                              
    right_shift = -self.get_descendant_count() - 2                      
    self._tree_manager._post_insert_update_cached_parent_right(parent, right_shift)
                                                                        
return super(MPTTModel, self).delete(*args, **kwargs)                   

我已经使用事务解决了这个问题。类似的东西:

from django.db import transaction

def delete(self, *args, **kwargs):
    with transaction.atomic():
        super().delete(*args, **kwargs)

在 shell 中测试(django 3.1.7 和 django-mptt 0.12.0)

from mptt.models import MPTTModel, TreeForeignKey
from django.db import models, transaction

class Test(MPTTModel):
    name = models.TextField()
    parent = TreeForeignKey(
        'self', 
        on_delete=models.PROTECT, 
        null=True,
        blank=True,
        related_name='children'
    )
from testing.models import Test

a = Test.objects.create(name='a')
b = Test.objects.create(name='b', parent=a)
c = Test.objects.create(name='c', parent=b)

print(a.get_descendants())
print(c.get_ancestors())

b.delete()

#  refresh from db
a = Test.objects.get(pk=a.pk)
c = Test.objects.get(pk=c.pk)

print(a.get_descendants())
print(c.get_ancestors())
  1. 无交易:
>>> print(a.get_descendants())
<TreeQuerySet [<Test: Test object (20)>, <Test: Test object (21)>]>
>>> print(c.get_ancestors())
<TreeQuerySet [<Test: Test object (19)>, <Test: Test object (20)>]>
>>> 
>>> b.delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/mptt/models.py", line 1138, in delete
    return super().delete(*args, **kwargs)
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/django/db/models/base.py", line 946, in delete
    collector.collect([self], keep_parents=keep_parents)
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/django/db/models/deletion.py", line 302, in collect
    raise ProtectedError(
django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'Test' because they are referenced through protected foreign keys: 'Test.parent'.", {<Test: Test object (21)>})
>>> 
>>> #  refresh from db
>>> a = Test.objects.get(pk=a.pk)
>>> c = Test.objects.get(pk=c.pk)
>>> 
>>> print(a.get_descendants())
<TreeQuerySet []>
>>> print(c.get_ancestors())
<TreeQuerySet [<Test: Test object (20)>]>
  1. 交易内
>>> print(a.get_descendants())
<TreeQuerySet [<Test: Test object (23)>, <Test: Test object (24)>]>
>>> print(c.get_ancestors())
<TreeQuerySet [<Test: Test object (22)>, <Test: Test object (23)>]>
>>> 
>>> b.delete()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/vladimir/Desktop/test/someproject/testing/models.py", line 16, in delete
    super().delete(*args, **kwargs)
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/mptt/models.py", line 1138, in delete
    return super().delete(*args, **kwargs)
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/django/db/models/base.py", line 946, in delete
    collector.collect([self], keep_parents=keep_parents)
  File "/home/vladimir/Desktop/test/.venv/lib/python3.8/site-packages/django/db/models/deletion.py", line 302, in collect
    raise ProtectedError(
django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'Test' because they are referenced through protected foreign keys: 'Test.parent'.", {<Test: Test object (24)>})
>>> 
>>> #  refresh from db
>>> a = Test.objects.get(pk=a.pk)
>>> c = Test.objects.get(pk=c.pk)
>>> 
>>> print(a.get_descendants())
<TreeQuerySet [<Test: Test object (23)>, <Test: Test object (24)>]>
>>> print(c.get_ancestors())
<TreeQuerySet [<Test: Test object (22)>, <Test: Test object (23)>]>