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 相关文章推荐
从CentOS安装完成到生成词云python的实例
Dec 01 Python
解决Python运行文件出现out of memory框的问题
Dec 03 Python
python 检查是否为中文字符串的方法
Dec 28 Python
python conda操作方法
Sep 11 Python
python脚本后台执行方式
Dec 21 Python
Django 限制访问频率的思路详解
Dec 24 Python
TensorFlow2.X结合OpenCV 实现手势识别功能
Apr 08 Python
django admin管理工具自定义时间区间筛选器DateRangeFilter介绍
May 19 Python
Django框架请求生命周期实现原理
Nov 13 Python
Python开发.exe小工具的详细步骤
Jan 27 Python
用Python爬取某乎手机APP数据
Jun 15 Python
教你使用Python获取QQ音乐某个歌手的歌单
Apr 03 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加密解密函数详解
2015/10/28 PHP
利用Laravel事件系统如何实现登录日志的记录详解
2017/05/20 PHP
JavaScript事件列表解说
2006/12/22 Javascript
JQuery 选择和过滤方法代码总结
2010/11/19 Javascript
vue.js+boostrap项目实践(案例详解)
2016/09/21 Javascript
js实现控制textarea输入字符串的个数,鼠标按下抬起判断输入字符数
2016/10/25 Javascript
Angular 常用指令实例总结整理
2016/12/13 Javascript
JS请求servlet功能示例
2017/06/01 Javascript
js判断节假日实例代码
2017/12/27 Javascript
layui中使用jquery控制radio选中事件的示例代码
2018/08/15 jQuery
layer弹出层父子页面事件相互调用方法
2018/08/17 Javascript
ES6知识点整理之模块化的应用详解
2019/04/15 Javascript
Python捕捉和模拟鼠标事件的方法
2015/06/03 Python
python模块之sys模块和序列化模块(实例讲解)
2017/09/13 Python
Numpy之文件存取的示例代码
2018/08/03 Python
python连接mongodb密码认证实例
2018/10/16 Python
Django框架中间件(Middleware)用法实例分析
2019/05/24 Python
Python使用Pandas对csv文件进行数据处理的方法
2019/08/01 Python
python 工具 字符串转numpy浮点数组的实现
2020/03/14 Python
python 在threading中如何处理主进程和子线程的关系
2020/04/25 Python
Python自定义聚合函数merge与transform区别详解
2020/05/26 Python
html5 worker 实例(一) 为什么测试不到效果
2013/06/24 HTML / CSS
一个不错的HTML5 Canvas多层点击事件监听实例
2014/04/29 HTML / CSS
亚洲航空公司官方网站:AirAsia
2019/11/25 全球购物
Happy Socks英国官网:购买五颜六色的袜子
2020/11/03 全球购物
函授大学生自我鉴定
2014/02/05 职场文书
《画风》教学反思
2014/04/16 职场文书
保护环境的标语
2014/06/09 职场文书
初中优秀教师事迹材料
2014/08/18 职场文书
个人四风对照检查材料
2014/09/26 职场文书
党政领导班子四风问题对照检查材料思想汇报
2014/10/02 职场文书
党员干部反四风民主生活会对照检查材料思想汇报
2014/10/12 职场文书
2014会计年终工作总结
2014/12/20 职场文书
2015年优质护理服务工作总结
2015/04/08 职场文书
想创业成功,需要掌握这些要点
2019/12/06 职场文书
关于SpringBoot 使用 Redis 分布式锁解决并发问题
2021/11/17 Redis