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进程通信之匿名管道实例讲解
Apr 11 Python
python:socket传输大文件示例
Jan 18 Python
详解supervisor使用教程
Nov 21 Python
python实现神经网络感知器算法
Dec 20 Python
Python网络爬虫之爬取微博热搜
Apr 18 Python
快速解决vue.js 模板和jinja 模板冲突的问题
Jul 26 Python
python使用正则来处理各种匹配问题
Dec 22 Python
Python 限定函数参数的类型及默认值方式
Dec 24 Python
Python求平面内点到直线距离的实现
Jan 19 Python
python日期与时间戳的各种转换示例
Feb 12 Python
基于python 凸包问题的解决
Apr 16 Python
Python常用数据分析模块原理解析
Jul 20 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
PHP5+UTF8多文件上传类
2008/10/17 PHP
getimagesize获取图片尺寸实例
2014/11/15 PHP
PHP中类型转换 ,常量,系统常量,魔术常量的详解
2017/10/26 PHP
Laravel 5.4前后台分离,通过不同的二级域名访问方法
2019/10/13 PHP
jQuery设置div一直在页面顶部显示的方法
2013/10/24 Javascript
javascript上下方向键控制表格行选中并高亮显示的方法
2015/02/13 Javascript
JavaScript定时显示广告代码分享
2015/03/02 Javascript
JS实现FLASH幻灯片图片切换效果的方法
2015/03/04 Javascript
Nodejs实战心得之eventproxy模块控制并发
2015/10/27 NodeJs
简单实现的JQuery文本框水印插件
2016/06/14 Javascript
再次谈论Javascript中的this
2016/06/23 Javascript
自己封装的一个原生JS拖动方法(推荐)
2016/11/22 Javascript
Bootstrap实现圆角、圆形头像和响应式图片
2016/12/14 Javascript
Node.js设置CORS跨域请求中多域名白名单的方法
2017/03/28 Javascript
json的结构与遍历方法实例分析
2017/04/25 Javascript
ECMAScript6变量的解构赋值实例详解
2017/09/19 Javascript
微信小程序如何像vue一样在动态绑定类名
2018/04/17 Javascript
JavaScript学习教程之cookie与webstorage
2019/06/23 Javascript
浅谈JavaScript 声明提升
2020/09/14 Javascript
如何正确解决VuePress本地访问出现资源报错404的问题
2020/12/03 Vue.js
python网络爬虫采集联想词示例
2014/02/11 Python
python生成器generator用法实例分析
2015/06/04 Python
Python  Django 母版和继承解析
2019/08/09 Python
python获取Linux发行版名称
2019/08/30 Python
python 基于dlib库的人脸检测的实现
2019/11/08 Python
将python依赖包打包成window下可执行文件bat方式
2019/12/26 Python
有关pycharm登录github时有的时候会报错connection reset的问题
2020/09/15 Python
法国二手MacBook销售网站:Okamac
2019/03/18 全球购物
经济与贸易专业应届生求职信
2013/11/19 职场文书
办公室员工岗位工作职责
2014/03/10 职场文书
学校党的群众路线教育实践活动整改措施
2014/10/25 职场文书
2014年生产管理工作总结
2014/12/23 职场文书
人事专员岗位职责
2015/02/03 职场文书
600字作文之感受大自然
2019/11/27 职场文书
如何使用php生成zip压缩包
2021/04/21 PHP
golang内置函数len的小技巧
2021/07/25 Golang