django 实现:闭包表—树状结构

闭包表—树状结构数据的数据库表设计

闭包表模型

闭包表(Closure Table)是一种通过空间换时间的模型,它是用一个专门的关系表(其实这也是我们推荐的归一化方式)来记录树上节点之间的层级关系以及距离。

场景

我们 基于 django orm实现一个文件树,文件夹直接可以实现无限嵌套
在这里插入图片描述

models

#  文件详情表(主要用于记录文件名)
class DmFileDetail(models.Model):file_name = models.CharField("文件名(文件夹名)", max_length=50)is_file = models.BooleanField("是否是文件", default=False)user_id = models.IntegerField("用户id", default=0)create_time = models.IntegerField("创建时间", default=0)update_time = models.IntegerField("创建时间", default=0)is_del = models.BooleanField("是否删除", default=False)class Meta:db_table = 'inchat_dm_file_detail'verbose_name = verbose_name_plural = u'数字人文件详情表'def __str__(self):return self.file_name#  文件关系表(主要用户记录文件之间的关联,即路径)
class DmFileRelation(models.Model):ancestor_id = models.IntegerField("祖先节点ID")descendant_id = models.IntegerField("子孙节点ID")depth = models.IntegerField("深度(层级)", db_index=True)user_id = models.IntegerField("用户id", default=0, db_index=True)is_del = models.BooleanField("是否删除", default=False)class Meta:db_table = 'inchat_dm_file_relation'index_together = ('ancestor_id', 'descendant_id')verbose_name = verbose_name_plural = u'数字人文件关系表'
idfile_name
1AAA
2aaa.pdf
idancestor_iddescendant_iddepth
1110
2220
3121

增删改查

class DmRelationNode:"""关系节点"""NAME = "DmRelationNode"RELATION_CLIENT = DmFileRelation@classmethoddef insert_relation_node(cls, node_id, user_id, parent_id):"""插入新的关系节点"""# 自身insert_self = cls.RELATION_CLIENT(ancestor_id=parent_id,descendant_id=node_id,user_id=user_id,depth=1)insert_list = []# 获取父节点所有祖先parent_relation = cls.RELATION_CLIENT.objects.filter(descendant_id=parent_id) \.values_list('ancestor_id', 'depth')for ancestor_id, depth in parent_relation:insert_data = cls.RELATION_CLIENT(ancestor_id=ancestor_id,descendant_id=node_id,depth=depth + 1,user_id=user_id)insert_list.append(insert_data)# 插入自身insert_list.append(insert_self)logger.info('%s insert_relation_node.node_id:%s,parent_id:%s,insert_list:%s', cls.NAME, node_id, parent_id,insert_list)ret = cls.RELATION_CLIENT.objects.bulk_create(insert_list)logger.info('%s insert_relation_node.node_id:%s,parent_id:%s,ret_list:%s', cls.NAME, node_id, parent_id, ret)return ret@classmethoddef get_ancestor_relation(cls, node_id):"""获取某个节点的所有祖先节点"""arges = ['ancestor_id', 'descendant_id', 'depth']ancestor_relation_list = cls.RELATION_CLIENT.objects.filter(descendant_id=node_id, is_del=False).values(*arges)relation_map = dict()relation_dict = relation_mapfor ancestor in ancestor_relation_list:relation_dict['id'] = ancestor['ancestor_id']if ancestor['ancestor_id'] != node_id:relation_dict['children'] = {}relation_dict = relation_dict['children']return ancestor_relation_list@classmethoddef get_descendant_relation(cls, node_id):"""获取所有的子节点"""arges = ['ancestor_id', 'descendant_id', 'depth']descendant_relation_list = cls.RELATION_CLIENT.objects.filter(ancestor_id=node_id, is_del=False).values(*arges)return descendant_relation_list@classmethoddef get_direct_relation(cls, user_id):"""获取所有直系"""arges = ['ancestor_id', 'descendant_id', 'depth']direct_relation = cls.RELATION_CLIENT.objects.filter(depth=1, user_id=user_id, is_del=False).values(*arges)return direct_relation@classmethoddef get_children_node(cls, node_id):"""获取某节点的子节点"""children_node = cls.RELATION_CLIENT.objects.filter(depth=1, ancestor_id=node_id, is_del=False) \.values_list('descendant_id', flat=True)return children_node@classmethoddef remove_node(cls, node_id):"""删除节点"""logger.info('%s remove_node. node_id:%s', cls.NAME, node_id)query = Q(ancestor_id=node_id, is_del=False) | Q(descendant_id=node_id, is_del=False)res = cls.RELATION_CLIENT.objects.filter(query).update(is_del=True)logger.info('%s remove_node. node_id:%s,count:%s', cls.NAME, node_id, res)return res

以下 是一些常规的操作

class DmFileTree:"""DM文件树"""NAME = "DmFileTree"DETAIL_CLIENT = DmFileDetailRELATION_NODE_CLIENT = DmRelationNodeFILE_SAVE_DIR = 'media/dm/'@classmethoddef get_file_map(cls, user_id):"""获取用户所有文件文件名"""file_detail = cls.DETAIL_CLIENT.objects.filter(user_id=user_id).values('id', 'file_name', 'path', 'is_file')file_map = dict()for file in file_detail:file_dict = dict(id=file['id'],name=file['file_name'],is_file=file['is_file'],filePath=cls.FILE_SAVE_DIR + file['path'] + file['file_name'])file_map[file['id']] = file_dictreturn file_map@classmethoddef add_file(cls, user_id, file_name, parent_id, path='', is_file=False):"""新建文件(夹)"""kwargs = dict(file_name=file_name,path=path,is_file=is_file,user_id=user_id,create_time=get_cur_timestamp())file_obj = cls.DETAIL_CLIENT.objects.create(**kwargs)if not file_obj:logger.error('%s add_file failed. kwargs:%s', cls.NAME, kwargs)return Falseres = cls.RELATION_NODE_CLIENT.insert_relation_node(node_id=file_obj.id, user_id=user_id, parent_id=parent_id)if not res:return Falsereturn dict(id=file_obj.id, name=file_name)@classmethoddef get_file_path(cls, file_id):"""获取文件路径"""ancestor_query = cls.RELATION_NODE_CLIENT.get_ancestor_relation(file_id)ancestor = map(lambda x: x['ancestor_id'], ancestor_query)# 过滤0ancestor = list(filter(lambda x: x > 0, ancestor))# 排序ancestor.sort()path = '/'.join(map(str, ancestor))return '/' + path + '/' if path else '/'@classmethoddef get_all_files(cls, user_id):# 获取所有文件名字典file_map = cls.get_file_map(user_id)# 查询所有子目录及文件files_relation_list = cls.RELATION_NODE_CLIENT.get_direct_relation(user_id)file_info = {a['descendant_id']: file_map.get(a['descendant_id']) or {} for a in files_relation_list}tree = cls.list_to_tree(files_relation_list, file_info)return tree@classmethoddef get_child_files(cls, user_id, parent_id):"""获取下级文件"""# 获取所有文件名字典file_map = cls.get_file_map(user_id)file_list = cls.RELATION_NODE_CLIENT.get_children_node(node_id=parent_id)files = map(lambda x: dict(id=x, name=file_map.get(x) or ''), file_list)return files@staticmethoddef list_to_tree(data, node_dict):"""将节点列表转换成树形结构字典:param data: 带有 id 和 parent_id 属性的节点列表:param node_dict: 单节点的数据结构字典:return: 树形结构字典"""tree = []# 遍历每一个节点,将其添加到父节点的字典或根节点列表中for item in data:id = item['descendant_id']parent_id = item['ancestor_id']# 如果父节点为 None,则将当前节点添加到根节点列表中if not parent_id:tree.append(node_dict[id])# 如果父节点存在,则将当前节点添加到父节点的 children 属性中else:parent = node_dict[parent_id]if 'children' not in parent:parent['children'] = []parent['children'].append(node_dict[id])return tree@classmethoddef delete_file(cls, file_id):"""文件删除"""res1 = cls.DETAIL_CLIENT.objects.filter(id=file_id).update(is_del=True)logger.info('%s delete_file. file_id:%s, count:%s', cls.NAME, file_id, res1)res2 = cls.RELATION_NODE_CLIENT.remove_node(file_id)return res1, res2@classmethoddef search_file(cls, file_name):"""搜索文件"""query_set = cls.DETAIL_CLIENT.objects.filter(file_name__icontains=file_name) \.values('id', 'file_name', 'path', 'is_file')file_list = []for file in query_set:file_dict = dict(id=file['id'],name=file['file_name'],is_file=file['is_file'],filePath='media/dm_upload' + file['path'])file_list.append(file_dict)return file_list@classmethoddef get_file_url(cls, file_id, file_obj=None):"""获取文件下载链接"""file_url = ''if not file_obj:file_obj = cls.DETAIL_CLIENT.objects.filter(id=file_id).first()if file_obj:file_url = 'http://127.0.0.1:8000/' + cls.FILE_SAVE_DIR + file_obj.path + file_obj.file_namereturn file_url

除此之外,还有移动、复制文件(夹)。移动就是先删除再新增

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/145125.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

红米手机 导出 通讯录 到电脑保存

不要搞什么 云服务 不要安装什么 手机助手 不要安装 什么app 用 usb 线 连接 手机 和 电脑 手机上会跳出 提示 选择 仅传输文件 会出现下面的 一个 盘 进入 MIUI目录 然后进入 此电脑\Redmi Note 5\内部存储设备\MIUI\backup\AllBackup\20230927_043337 如何没有上面的文件&a…

MyBatis的一级缓存和二级缓存:原理和作用

MyBatis的一级缓存和二级缓存:原理和作用 引言 在数据库访问中,缓存是一种重要的性能优化手段,它可以减少数据库查询的次数,加快数据访问速度。MyBatis作为一款流行的Java持久层框架,提供了一级缓存和二级缓存来帮助…

C++(string类)

本节目标: 1、为什么要学习string类 2.标准库中的string类 3.vs和g下string结构说明 1.为什么学习string类 1.1 c语言中的字符串 C 语言中,字符串是以 \0 结尾的一些字符的集合,为了操作方便, C 标准库中提供了一些 str系列的…

1.6.C++项目:仿mudou库实现并发服务器之channel模块的设计

项目完整版在: 文章目录 一、channel模块:事件管理Channel类实现二、提供的功能三、实现思想(一)功能(二)意义(三)功能设计 四、代码(一)框架(二…

3.物联网射频识别,(高频)RFID应用ISO14443-2协议

一。ISO14443-2协议简介 1.ISO14443协议组成及部分缩略语 (1)14443协议组成(下面的协议简介会详细介绍) 14443-1 物理特性 14443-2 射频功率和信号接口 14443-3 初始化和防冲突 (分为Type A、Type B两种接口&…

深入理解操作系统- - 进程篇(1)

目录 进程解释: process in memory(进程在内存中包含什么) : 并发的进程: 进程定义: 个人定义: 书本定义: 进程状态: 进程何时离开CPU: 内部事件: 外部事件: 进…

卫星图像应用 - 洪水检测 使用DALI进行数据预处理

这篇文章是上一篇的延申。 运行环境:Google Colab 1. 当今的深度学习应用包含由许多串行运算组成的、复杂的多阶段数据处理流水线,仅依靠 CPU 处理这些流水线已成为限制性能和可扩展性的瓶颈。 2. DALI 是一个用于加载和预处理数据的库,可…

消息队列实现进程之间通信方式代码,现象

1、向消息队列中写入数据 #include<myhead.h>//消息结构体 typedef struct {long msgtype; //消息类型char data[1024]; //消息正文 }Msg_ds;#define SIZE sizeof(Msg_ds)-sizeof(long) //正文大小int main(int argc, const char *argv[]) {//1、创建…

raw智能照片处理工具DxO PureRAW mac介绍

DxO PureRAW Mac版是一款raw智能照片处理工具&#xff0c;该软件采用了智能技术&#xff0c;以解决影响所有RAW文件的七个问题&#xff1a;去马赛克&#xff0c;降噪&#xff0c;波纹&#xff0c;变形&#xff0c;色差&#xff0c;不想要的渐晕&#xff0c;以及缺乏清晰度。 Dx…

渗透测试之打点

请遵守中华人民共和国网络安全法 打点的目的是获取一个服务器的控制权限 1. 企业架构收集 &#xff08;1&#xff09;官网 &#xff08;2&#xff09;网站或下属的子网站&#xff0c;依次往下 天眼查 企查查 2. ICP 备案查询 ICP/IP地址/域名信息备案管理系统 使用网站…

基于微信小程序的宠物寄养平台小程序设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

浏览器输入 URL 并回车发生了什么

本文节选自我的博客&#xff1a;浏览器输入 URL 并回车发生了什么 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是MilesChen&#xff0c;偏前端的全栈开发者。&#x1f4dd; CSDN主页&#xff1a;爱吃糖的猫&#x1f525;&#x1f4e3; 我的博客&#xff1a;爱吃糖…

国庆9.30

消息队列实现进程间通信 snd #include <myhead.h> //消息结构体 typedef struct {long msgtype; //消息类型char data[1024]; //消息正文 }Msg_ds;#define SIZE sizeof(Msg_ds)-sizeof(long) //正文大小int main(int argc, const char *argv[]) {//1、创建key…

常用数学分布

正态分布&#xff08;高斯分布&#xff09; 若随机变数 X X X 服从一个期望 μ \mu μ&#xff0c;标准差 的正态分布 σ \sigma σ&#xff0c;则记为 X ≈ N ( μ , σ 2 ) X \approx N(\mu,\sigma^2) X≈N(μ,σ2)&#xff0c;其密度函数为&#xff1a; f ( x ) 1 σ …

JSP学习笔记【三】——JQuery

前言 在写项目的时候需要动态对某组件的属性进行调整&#xff0c;我看网上的教程都是使用document.getElementById等&#xff0c;但我在eclipse编写.jsp文件的时候&#xff0c;却提示document cannot be resolved。由于我对jsp没有系统的了解以及无人可咨询&#xff0c;网上也…

lv5 嵌入式开发-10 信号机制(下)

目录 1 信号集、信号的阻塞 2 信号集操作函数 2.1 自定义信号集 2.2 清空信号集 2.3 全部置1 2.4 将一个信号添加到集合中 2.5 将一个信号从集合中移除 2.6 判断一个信号是否在集合中 2.7 设定对信号集内的信号的处理方式(阻塞或不阻塞) 2.8 使进程挂起&#xff08;…

仿真数据检查器如何比较数据

可以定制仿真数据检查器比较过程&#xff0c;以多种方式满足您的需求。在比较各运行时&#xff0c;仿真数据检查器会执行以下操作&#xff1a; 根据对齐设置&#xff0c;对齐基线运行和比较项运行中的信号对组。 仿真数据检查器不会比较无法对齐的信号。 根据指定的同步方法同…

机器学习——一元线性回归构造直线,并给出损失函数

目 录 Question 问题分析 1.概念补充 2.流程分析 3.注意 具体实现 最终成果 代码 思考&#xff1a; Question 在二维平面有n个点&#xff0c;如何画一条直线&#xff0c;使得所有点到该直线距离之和最短 如果能找到&#xff0c;请给出其损失函数 问题分析 1.概念…

数据集笔记:杭州地铁刷卡数据

1 数据位置&#xff1a; 链接: https://pan.baidu.com/s/1iLHomv5NRodB_3jr7FcFow 提取码: arse 全球城市计算AI挑战赛_算法大赛_天池大赛-阿里云天池的赛制 (aliyun.com) 2 数据集介绍 杭州20190101至20190125共25天地铁刷卡数据记录&#xff0c;共涉及3条线路81个地铁站约…

【Linux系统编程】进程状态

文章目录 前言1. 准备工作2. 阻塞、挂起状态的了解2.1 阻塞2.2 挂起 3. 看看Linux内核源代码怎么说4. R运行状态&#xff08;running&#xff09;5. S休眠状态&#xff08;sleeping)6. D不可中断休眠状态7. T暂停状态&#xff08;stopped&#xff09;8. t 追踪暂停状态 (tracin…