本文介绍了在Django中使用ClosureTable存储无限层次数据。有点三级分布的意思),需要用到闭包表的结构来存储。那么在Django中如何处理这种结构的模型呢?定义一个模型至少需要两个模型,一个用于存储类别,一个用于存储类别之间的关系:classCategory(models.Model):name=models.CharField(max_length=31)def__str__(self):returnself。nameclassCategoryRelation(models.Model):ancestor=models.ForeignKey(Category,null=True,related_name='ancestors',on_delete=models.SET_NULL,db_constraint=False,verbose_name='ancestor')后代=models.ForeignKey(Category,null=True,related_name='descendants',on_delete=models.SET_NULL,db_constraint=False,verbose_name='descendants')distance=models.IntegerField()classMeta:unique_together=("ancestor","descendant")数据操作得到所有后代节点类Category(models.Model):...defget_descendants(self,include_self=False):"""获取所有后代节点"""kw={'descendants__ancestor':self}ifnotinclude_self:kw['descendants__distance__gt']=0qs=Category.objects.filter(**kw).order_by('descendants__distance')返回qs得到直属类Category(models.Model):...defget_children(self):"""获取直接下属"""qs=Category.objects.filter(descendants__ancestor=self,descendants__distance=1)returnqs节点的移动节点的移动比较困难。https://www.percona.com/blog/...中有介绍,使用django执行nativesql语句,执行的是:defadd_child(self,child):"""给这个分类添加一个分类,"""如果CategoryRelation.objects.filter(ancestor=child,descendant=self).exists()\或CategoryRelation。objects.filter(ancestor=self,descendant=child,distance=1).exists():"""child不能是self的祖先节点或者已经是父子节点了。"""return#如果是自己的节点如果不是CategoryRelation.objects.filter(ancestor=child,descendant=child).exists():CategoryRelation.objects.create(ancestor=child,descendant=child,distance=0)table_name=CategoryRelation.表中不存在数据。_meta.db_table游标=连接。cursor()cursor.execute(f"""DELETEaFROM{table_name}ASaJOIN{table_name}ASdONa.descendant_id=d.descendant_idLEFTJOIN{table_name}ASxONx.ancestor_id=d.ancestor_idANDx.descendant_id=a.ancestor_idWHEREd.ancestor_id={child.id}ANDx.ancestor_idISNULL;""")cursor.execute(f"""INSERTINTO{table_name}(ancestor_id,descendant_id,distance)SELECTsupertree.ancestor_id,subtree.descendant_id,supertree.distance+subtree.distance+1FROM{table_name}ASsupertreeJOIN{table_name}作为子树WHEREsubtree.ancestor_id={child.id}ANDsupertree.descendant_id={self.id};""")删除节点删除节点有两种操作,一种是删除所有子节点,另一种是将自己的点移动到父节点延伸阅读https://www.percona.com/blog/...http://technobytz.com/closure...完整代码类Category(models.Model):name=models.CharField(max_length=31)def__str__(self):returnself.namedefget_descendants(self,include_self=False):"""获取所有后代节点"""kw={'descendants__ancestor':self}ifnotinclude_self:kw['descendants__distance__gt']=0qs=Category.objects.filter(**kw).order_by('descendants__distance')returnqsdefget_children(self):"""获取直属下属"""qs=Category.objects.filter(descendants__ancestor=self,descendants__distance=1)returnqsdefget_ancestors(self,include_self=False):"""获取所有祖先节点"""kw={'ancestors__descendant':self}ifnotinclude_self:kw['ancestors__distance__gt']=0qs=Category.objects.filter(**kw).order_by('ancestors__distance')returnqsdefget_parent(self):"""Category只有一个父节点"""parent=Category.objects.get(ancestors__descendant=self,ancestors__distance=1)returnparentdefget_parents(self):"""Category只有一个父节点"""qs=Category.objects.filter(ancestors__descendant=self,ancestors__distance=1)returnqsdefremove(self,delete_subtree=False):"""删除节点"""ifdelete_subtree:#删除所有子节点children_queryset=self.get_descendants(include_self=True)forchildinchildren_queryset:CategoryRelation.objects.filter(Q(ancestor=child)|Q(descendant=child)).delete()child.delete()else:#将所有子节点移动到父节点parent=self.get_parent()children=self.get_children()forchildinchildren:parent.add_chile(child)#CategoryRelation.objects.filter(descendant=self,distance=0).delete()CategoryRelation.objects.filter(Q(ancestor=self)|Q(descendant=self)).delete()self.delete()defadd_child(self,child):"""给这个分类添加一个分类,"""ifCategoryRelation.objects.filter(ancestor=child,descendant=self).exists()\orCategoryRelation.objects.filter(ancestor=self,descendant=child,distance=1).exists():"""child不能是self的祖先节点或者它们已经是父子节点““关于turn#如果节点自身的数据在表中不存在ifnotCategoryRelation.objects.filter(ancestor=child,descendant=child).exists():CategoryRelation.objects.create(ancestor=child,descendant=child,distance=0)table_name=CategoryRelation._meta.db_tablecursor=connection.cursor()cursor.execute(f"""删除aFROM{table_name}ASaJOIN{table_name}ASdONa.descendant_id=d.descendant_idLEFTJOIN{table_name}ASxONx.ancestor_id=d.ancestor_idANDx.descendant_id=a.ancestor_idWHEREd.ancestor_id={child.id}ANDx.ancestor_idISNULL;""")cursor.execute(f"""INSERTINTO{table_name}(ancestor_id,descendant_id,distance)SELECTsupertree.ancestor_id,subtree.descendant_id,supertree.distance+subtree。distance+1FROM{table_name}ASsupertreeJOIN{table_name}ASsubtreeWHEREsubtree.ancestor_id={child.id}ANDsupertree.descendant_id={self.id};""")classCategoryRelation(models.Model):祖先=models.ForeignKey(Category,null=True,related_name='ancestors',on_delete=models.SET_NULL,db_constraint=False,verbose_name='祖先')descendant=models.ForeignKey(Category,null=True,related_name='descendants',on_delete=models.SET_NULL,db_constraint=False,verbose_name='子孙')distance=models.IntegerField()classMeta:unique_together=("祖先,",“后代”)[1]:https://www.percona.com/blog/2011/02/14/moving-subtrees-in-closure-table/[2]:https://www.percona.com/博客/2011/02/14/moving-subtrees-in-closure-table/[3]:http://technobytz.com/closure_table_store_hierarchical_data.html
