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爬虫之抓取百度贴吧并存储到本地txt文件改进版
Nov 06 Python
Python找出文件中使用率最高的汉字实例详解
Jun 03 Python
python实现FTP服务器服务的方法
Apr 11 Python
python实现八大排序算法(2)
Sep 14 Python
python sklearn库实现简单逻辑回归的实例代码
Jul 01 Python
Python使用Pandas读写Excel实例解析
Nov 19 Python
pyinstaller还原python代码过程图解
Jan 08 Python
Python 识别12306图片验证码物品的实现示例
Jan 20 Python
pycharm实现在子类中添加一个父类没有的属性
Mar 12 Python
Keras之自定义损失(loss)函数用法说明
Jun 10 Python
Python下opencv库的安装过程及问题汇总
Jun 11 Python
Python中的socket网络模块介绍
Jul 23 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读取csc文件并输出
2015/05/21 PHP
YII CLinkPager分页类扩展增加显示共多少页
2016/01/29 PHP
php封装一个异常的处理类
2017/06/08 PHP
超级简单的图片防盗(HTML),好用
2007/04/08 Javascript
js获取元素在浏览器中的绝对位置
2010/07/24 Javascript
javascript结合CSS实现苹果开关按钮特效
2015/04/07 Javascript
jquery简单实现外部链接用新窗口打开的方法
2015/05/30 Javascript
多种JQuery循环滚动文字图片效果代码
2020/06/23 Javascript
jQuery和JavaScript节点插入元素的方法对比
2016/11/18 Javascript
常用JS图片滚动(无缝、平滑、上下左右滚动)代码大全(推荐)
2016/12/20 Javascript
JavaScript实现分页效果
2017/03/28 Javascript
ES6深入理解之“let”能替代”var“吗?
2017/06/28 Javascript
Angular中自定义Debounce Click指令防止重复点击
2017/07/26 Javascript
bootstrap时间控件daterangepicker使用方法及各种小bug修复
2017/10/25 Javascript
Vue-Router实现组件间跳转的三种方法
2017/11/07 Javascript
Vue项目中使用Vux的安装过程
2018/05/01 Javascript
layer实现弹出层自动调节位置
2019/09/05 Javascript
关于layui 弹出层一闪而过就消失的解决方法
2019/09/09 Javascript
使用Python程序抓取新浪在国内的所有IP的教程
2015/05/04 Python
python的exec、eval使用分析
2017/12/11 Python
Python解析命令行读取参数--argparse模块使用方法
2018/01/23 Python
Python2中文处理纪要的实现方法
2018/03/10 Python
python启动应用程序和终止应用程序的方法
2019/06/28 Python
关于Keras Dense层整理
2020/05/21 Python
Python+PyQt5实现灭霸响指功能
2020/05/25 Python
Python爬虫入门有哪些基础知识点
2020/06/02 Python
CSS3实现背景透明文字不透明的示例代码
2018/06/25 HTML / CSS
html5画布旋转效果示例
2014/01/27 HTML / CSS
canvas拼图功能实现代码示例
2018/11/21 HTML / CSS
基于HTML5+tracking.js实现刷脸支付功能
2020/04/16 HTML / CSS
任意存:BOXFUL
2018/05/21 全球购物
简述DNS进行域名解析的过程
2013/12/02 面试题
继承权公证书
2014/04/09 职场文书
创业计划书之物流运送
2019/09/17 职场文书
Python源码解析之List
2021/05/21 Python
python中的plt.cm.Paired用法说明
2021/05/31 Python