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 相关文章推荐
python生成IP段的方法
Jul 07 Python
在Django中创建第一个静态视图
Jul 15 Python
Python实现快速排序和插入排序算法及自定义排序的示例
Feb 16 Python
PyQt5每天必学之切换按钮
Aug 20 Python
Python实现的文本对比报告生成工具示例
May 22 Python
对pandas中to_dict的用法详解
Jun 05 Python
pygame游戏之旅 计算游戏中躲过的障碍数量
Nov 20 Python
浅谈python中真正关闭socket的方法
Dec 18 Python
在 Linux/Mac 下为Python函数添加超时时间的方法
Feb 20 Python
Keras模型转成tensorflow的.pb操作
Jul 06 Python
Python中qutip用法示例详解
Oct 02 Python
pytorch 实现在测试的时候启用dropout
May 27 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
PHP脚本的10个技巧(5)
2006/10/09 PHP
JQuery 小练习(实例代码)
2009/08/07 Javascript
javascript 操作cookies及正确使用cookies的属性
2009/10/15 Javascript
轻量级 JS ToolTip提示效果
2010/07/20 Javascript
解析jQuery的三种bind/One/Live事件绑定使用方法
2013/12/30 Javascript
基于 Docker 开发 NodeJS 应用
2014/07/30 NodeJs
JavaScript实现将xml转换成html table表格的方法
2015/04/17 Javascript
基于jQuery实现的向下滑动二级菜单效果代码
2015/08/31 Javascript
最简单纯JavaScript实现Tab标签页切换的方式(推荐)
2016/07/25 Javascript
详解webpack 配合babel 将es6转成es5 超简单实例
2017/05/02 Javascript
angular2 ng build部署后base文件路径问题详细解答
2017/07/15 Javascript
JavaScript实现各种排序的代码详解
2017/08/28 Javascript
使用Electron构建React+Webpack桌面应用的方法
2017/12/15 Javascript
微信小程序排坑指南详解
2018/05/23 Javascript
JS Ajax请求会话过期处理问题解决方法分析
2019/11/16 Javascript
微信小程序 SOTER 生物认证DEMO 指纹识别功能
2019/12/13 Javascript
[46:25]DOTA2上海特级锦标赛主赛事日 - 4 败者组第五轮 MVP.Phx VS EG第二局
2016/03/05 DOTA
使用Python的Supervisor进行进程监控以及自动启动
2014/05/29 Python
Python 转义字符详细介绍
2017/03/21 Python
python 输出所有大小写字母的方法
2019/01/02 Python
记录一下scrapy中settings的一些配置小结
2020/09/28 Python
实例教程 纯CSS3打造非常炫的加载动画效果
2014/11/05 HTML / CSS
HTML5 Canvas实现玫瑰曲线和心形图案的代码实例
2014/04/10 HTML / CSS
DJI大疆无人机官方商城:全球领先的无人飞行器研发和生产商
2016/12/21 全球购物
How to spawning asynchronous work in J2EE
2016/08/29 面试题
JVM是一个编译程序还是解释程序
2012/09/11 面试题
最新计算机专业自荐信
2013/10/16 职场文书
园林施工员岗位职责
2013/12/11 职场文书
消防工作实施方案
2014/06/09 职场文书
低碳环保口号
2014/06/12 职场文书
中药学专业毕业生推荐信
2014/07/10 职场文书
学党史心得体会
2014/09/05 职场文书
对党的十八届四中全会的期盼
2014/10/17 职场文书
2014离婚协议书范文(3篇)
2014/11/29 职场文书
保研推荐信范文
2015/03/25 职场文书
应聘教师自荐信
2015/03/26 职场文书