Django中使用 Closure Table 储存无限分级数据


Posted in Python onJune 06, 2019

这篇文章给大家介绍Django中使用 Closure Table 储存无限分级数据,具体内容如下所述:

起步

对于数据量大的情况(比如用户之间有邀请链,有点三级分销的意思),就要用到 closure table 的结构来进行存储。那么在 Django 中如何处理这个结构的模型呢?

定义模型

至少是要两个模型的,一个是存储分类,一个储存分类之间的关系:

class Category(models.Model):
 name = models.CharField(max_length=31)
 def __str__(self):
 return self.name
class CategoryRelation(models.Model):
 ancestor = 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()
 class Meta:
 unique_together = ("ancestor", "descendant")

数据操作

获得所有后代节点

class Category(models.Model):
 ...
 def get_descendants(self, include_self=False):
 """获得所有后代节点"""
 kw = {
 'descendants__ancestor' : self
 }
 if not include_self:
 kw['descendants__distance__gt'] = 0
 qs = Category.objects.filter(**kw).order_by('descendants__distance')
 return qs获得直属下级
class Category(models.Model):
 ...
 def get_children(self):
 """获得直属下级"""
 qs = Category.objects.filter(descendants__ancestor=self, descendants__distance=1)
 return qs

节点的移动

节点的移动是比较难的,在 [ https://www.percona.com/blog/2011/02/14/moving-subtrees-in-closure-table/][1 ] 中讲述了,利用django能够执行原生的sql语句进行:

def add_child(self, child):
 """将某个分类加入本分类,"""
 if CategoryRelation.objects.filter(ancestor=child, descendant=self).exists() \
 or CategoryRelation.objects.filter(ancestor=self, descendant=child, distance=1).exists():
 """child不能是self的祖先节点 or 它们已经是父子节点"""
 return
 # 如果表中不存在节点自身数据
 if not CategoryRelation.objects.filter(ancestor=child, descendant=child).exists():
 CategoryRelation.objects.create(ancestor=child, descendant=child, distance=0)
 table_name = CategoryRelation._meta.db_table
 cursor = connection.cursor()
 cursor.execute(f"""
 DELETE a
 FROM
 {table_name} AS a
 JOIN {table_name} AS d ON a.descendant_id = d.descendant_id
 LEFT JOIN {table_name} AS x ON x.ancestor_id = d.ancestor_id
 AND x.descendant_id = a.ancestor_id
 WHERE
 d.ancestor_id = {child.id}
 AND x.ancestor_id IS NULL;
 """)
 cursor.execute(f"""
 INSERT INTO {table_name} (ancestor_id, descendant_id, distance)
 SELECT supertree.ancestor_id, subtree.descendant_id,
 supertree.distance+subtree.distance+1
 FROM {table_name} AS supertree JOIN {table_name} AS subtree
 WHERE subtree.ancestor_id = {child.id}
 AND supertree.descendant_id = {self.id};
 """)

 节点删除

节点删除有两种操作,一个是将所有子节点也删除,另一个是将自己点移到上级节点中。

扩展阅读

[ https://www.percona.com/blog/2011/02/14/moving-subtrees-in-closure-table/][2 ]
[ http://technobytz.com/closure_table_store_hierarchical_data.html][3 ]

完整代码

class Category(models.Model):
name = models.CharField(max_length=31)
def __str__(self):
 return self.name
def get_descendants(self, include_self=False):
 """获得所有后代节点"""
 kw = {
 'descendants__ancestor' : self
 }
 if not include_self:
 kw['descendants__distance__gt'] = 0
 qs = Category.objects.filter(**kw).order_by('descendants__distance')
 return qs
def get_children(self):
 """获得直属下级"""
 qs = Category.objects.filter(descendants__ancestor=self, descendants__distance=1)
 return qs
def get_ancestors(self, include_self=False):
 """获得所有祖先节点"""
 kw = {
 'ancestors__descendant': self
 }
 if not include_self:
 kw['ancestors__distance__gt'] = 0
 qs = Category.objects.filter(**kw).order_by('ancestors__distance')
 return qs
def get_parent(self):
 """分类仅有一个父节点"""
 parent = Category.objects.get(ancestors__descendant=self, ancestors__distance=1)
 return parent
def get_parents(self):
 """分类仅有一个父节点"""
 qs = Category.objects.filter(ancestors__descendant=self, ancestors__distance=1)
 return qs
def remove(self, delete_subtree=False):
 """删除节点"""
 if delete_subtree:
 # 删除所有子节点
 children_queryset = self.get_descendants(include_self=True)
 for child in children_queryset:
 CategoryRelation.objects.filter(Q(ancestor=child) | Q(descendant=child)).delete()
 child.delete()
 else:
 # 所有子节点移到上级
 parent = self.get_parent()
 children = self.get_children()
 for child in children:
 parent.add_chile(child)
 # CategoryRelation.objects.filter(descendant=self, distance=0).delete()
 CategoryRelation.objects.filter(Q(ancestor=self) | Q(descendant=self)).delete()
 self.delete()
def add_child(self, child):
 """将某个分类加入本分类,"""
 if CategoryRelation.objects.filter(ancestor=child, descendant=self).exists() \
 or CategoryRelation.objects.filter(ancestor=self, descendant=child, distance=1).exists():
 """child不能是self的祖先节点 or 它们已经是父子节点"""
 return
 # 如果表中不存在节点自身数据
 if not CategoryRelation.objects.filter(ancestor=child, descendant=child).exists():
 CategoryRelation.objects.create(ancestor=child, descendant=child, distance=0)
 table_name = CategoryRelation._meta.db_table
 cursor = connection.cursor()
 cursor.execute(f"""
 DELETE a
 FROM
 {table_name} AS a
 JOIN {table_name} AS d ON a.descendant_id = d.descendant_id
 LEFT JOIN {table_name} AS x ON x.ancestor_id = d.ancestor_id
 AND x.descendant_id = a.ancestor_id
 WHERE
 d.ancestor_id = {child.id}
 AND x.ancestor_id IS NULL;
 """)
 cursor.execute(f"""
 INSERT INTO {table_name} (ancestor_id, descendant_id, distance)
 SELECT supertree.ancestor_id, subtree.descendant_id,
 supertree.distance+subtree.distance+1
 FROM {table_name} AS supertree JOIN {table_name} AS subtree
 WHERE subtree.ancestor_id = {child.id}
 AND supertree.descendant_id = {self.id};
 """)class CategoryRelation(models.Model): ancestor = 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()
class Meta:
 unique_together = ("ancestor", "descendant")[1]: https://www.percona.com/blog/2011/02/14/moving-subtrees-in-closure-table/
 [2]: https://www.percona.com/blog/2011/02/14/moving-subtrees-in-closure-table/
 [3]: http://technobytz.com/closure_table_store_hierarchical_data.html

总结

以上所述是小编给大家介绍的Django中使用 Closure Table 储存无限分级数据,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Python 相关文章推荐
ptyhon实现sitemap生成示例
Mar 30 Python
python实现获取序列中最小的几个元素
Sep 25 Python
基于Python Shell获取hostname和fqdn释疑
Jan 25 Python
Python2.7+pytesser实现简单验证码的识别方法
Dec 29 Python
OPENCV去除小连通区域,去除孔洞的实例讲解
Jun 21 Python
实时获取Python的print输出流方法
Jan 07 Python
Python爬取数据保存为Json格式的代码示例
Apr 09 Python
Python中面向对象你应该知道的一下知识
Jul 10 Python
PyTorch中常用的激活函数的方法示例
Aug 20 Python
Python随机函数库random的使用方法详解
Aug 21 Python
解决tensorflow读取本地MNITS_data失败的原因
Jun 22 Python
Python中requests做接口测试的方法
May 30 Python
创建Django项目图文实例详解
Jun 06 #Python
Django网络框架之HelloDjango项目创建教程
Jun 06 #Python
python操作小程序云数据库实现简单的增删改查功能
Jun 06 #Python
Django网络框架之创建虚拟开发环境操作示例
Jun 06 #Python
浅析Python3中的对象垃圾收集机制
Jun 06 #Python
聊聊python里如何用Borg pattern实现的单例模式
Jun 06 #Python
使用Python实现毫秒级抢单功能
Jun 06 #Python
You might like
Discuz 模板引擎的封装类代码
2008/07/18 PHP
php数据库备份还原类分享
2014/03/20 PHP
php图片水印添加、压缩、剪切的封装类实现
2020/04/18 PHP
PHP中常见的缓存技术实例分析
2015/09/23 PHP
php的socket编程详解
2016/11/20 PHP
ThinkPHP+EasyUI之ComboTree中的会计科目树形菜单实现方法
2017/06/09 PHP
PHP调用接口用post方法传送json数据的实例
2018/05/31 PHP
浅谈PHP SHA1withRSA加密生成签名及验签
2019/03/18 PHP
如何在Mozilla Gecko 用Javascript加载XSL
2007/01/09 Javascript
动态添加js事件实现代码
2009/03/12 Javascript
jQuery .tmpl(), .template()学习资料小结
2011/07/18 Javascript
Extjs中TabPane如何嵌套在其他网页中实现思路及代码
2013/01/27 Javascript
JQuery 操作/获取table具体代码
2013/06/13 Javascript
JavaScript实现的in_array函数
2014/08/27 Javascript
js实现感应鼠标图片透明度变化的方法
2015/02/20 Javascript
介绍JavaScript的一个微型模版
2015/06/24 Javascript
Angular的Bootstrap(引导)和Compiler(编译)机制
2016/06/20 Javascript
js日期相关函数dateAdd,dateDiff,dateFormat等介绍
2016/09/24 Javascript
js实现仿购物车加减效果
2017/03/01 Javascript
jquery validate 实现动态增加/删除验证规则操作示例
2019/10/28 jQuery
javascript设计模式 ? 状态模式原理与用法实例分析
2020/04/22 Javascript
从零学python系列之浅谈pickle模块封装和拆封数据对象的方法
2014/05/23 Python
Python解析nginx日志文件
2015/05/11 Python
Windows下的Jupyter Notebook 安装与自定义启动(图文详解)
2018/02/21 Python
python如何求解两数的最大公约数
2018/09/27 Python
对IPython交互模式下的退出方法详解
2019/02/16 Python
Python网络爬虫之爬取微博热搜
2019/04/18 Python
python flask 如何修改默认端口号的方法步骤
2019/07/12 Python
Pyecharts 中Geo函数常用参数的用法说明
2021/02/01 Python
处级干部反四风个人对照检查材料思想汇报
2014/09/27 职场文书
师德自我剖析材料范文
2014/10/06 职场文书
2015年依法治校工作总结
2015/07/27 职场文书
谢师宴学生致辞
2015/07/27 职场文书
关于食品安全的演讲稿范文(三篇)
2019/10/21 职场文书
SQL Server 数据库实验课第五周——常用查询条件
2021/04/05 SQL Server
Redis IP地址的绑定的实现
2021/05/08 Redis