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'数字人文件关系表'
| id | file_name |
|---|---|
| 1 | AAA |
| 2 | aaa.pdf |
| id | ancestor_id | descendant_id | depth |
|---|---|---|---|
| 1 | 1 | 1 | 0 |
| 2 | 2 | 2 | 0 |
| 3 | 1 | 2 | 1 |
增删改查
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
除此之外,还有移动、复制文件(夹)。移动就是先删除再新增
相关文章:
django 实现:闭包表—树状结构
闭包表—树状结构数据的数据库表设计 闭包表模型 闭包表(Closure Table)是一种通过空间换时间的模型,它是用一个专门的关系表(其实这也是我们推荐的归一化方式)来记录树上节点之间的层级关系以及距离。 场景 我们 …...
Redis与分布式-集群搭建
接上文 Redis与分布式-哨兵模式 1. 集群搭建 搭建简单的redis集群,创建6个配置,开启集群模式,将之前配置过的redis删除,重新复制6份 针对主节点redis 1,redis 2,redis 3都是以上修改内容,只是…...
C++--位图和布隆过滤器
1.什么是位图 所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。比如int 有32位,就可以存放0到31这32个数字在不在某个文件中。当然,其他类型也可以。 2.位…...
linux常识
目录 i.mx6ull开发板配置ip 静态IP配置 命令行配置 配置文件配置 动态IP配置 命令行配置 配置文件配置 为什么编译驱动程序之前要先编译内核? init系统服务 systemv守护进程 systemd守护进程 i.mx6ull开发板配置ip i.mx6ull有两个网卡(eth0和…...
Codeforces Round 901 (Div. 1) B. Jellyfish and Math(思维题/bfs)
题目 t(t<1e5)组样例,每次给出a,b,c,d,m(0<a,b,c,d,m<2的30次方) 初始时,(x,y)(a,b),每次操作,你可以执行以下四种操作之一 ①xx&y,&为与 ②xx|y,|为或 ③yx^y,^为异或 …...
unity 鼠标标记 左键长按生成标记右键长按清除标记,对象转化为子物体
linerender的标记参考 unity linerenderer在Game窗口中任意画线_游戏内编辑linerender-CSDN博客 让生成的标记转化为ARMarks游戏对象的子物体 LineMark.cs using System.Collections; using System.Collections.Generic; using UnityEngine;public class LineMark : MonoBeh…...
解决mac pro 连接4k显示器严重发烫、卡顿问题
介绍个不用花钱的方法。其实mac自带的风扇散热能力还可以的,但是默认比较懒散,可以用一个软件来控制下,激发下它的潜能。 可以下个stats软件 打开传感器开关,以及同步控制风扇开关 以及cpu显示温度 点击控制台上的温度图标&…...
QT的ui设计中改变样式表的用法
在QT的ui设计中,我们右键会弹出一个改变样式表的选项,很多人不知道这个是干什么的。 首先我们来看下具体的界面 首先我们说一下这个功能具体是干嘛的, 我们在设置很多控件在界面上之后,常常都是使用系统默认的样式,但是当有些时候为了美化界面我们需要对一些控件进行美化…...
零基础Linux_10(进程)进程终止(main函数的返回值)+进程等待
目录 1. 进程终止 1.1 main函数的返回值 1.2 进程退出码和错误码 1.3 进程终止的常见方法 2. 进程等待 2.1 进程等待的原因 2.2 wait 函数 2.3 waitpid 函数 2.4 int* status参数 2.5 int options非阻塞等待 本篇完。 1. 进程终止 进程终止指的就是程序执行结束了&…...
【已解决】opencv 交叉编译 ffmpeg选项始终为NO
一、opencv 交叉编译没有 ffmpeg ,会导致视频打不开 在交叉编译时候,发现在 pc 端能用 opencv 打开的视频,但是在 rv1126 上打不开。在网上查了很久,原因可能是 交叉编译过程 ffmpeg 造成的。之前 ffmpeg 是直接用 apt 安装的&am…...
rust生命期
一、生命期是什么 生命期,又叫生存期,就是变量的有效期。 实例1 {let r;{let x 5;r &x;}println!("r: {}", r); }编译错误,原因是r所引用的值已经被释放。 上图中的绿色范围’a表示r的生命期,蓝色范围’b表示…...
实现将一张图片中的目标图片抠出来
要在python中实现将一张图片中的目标图片裁剪出来,需要用到图像处理及机器学习库,以下是一个常用的基本框架 加载图片并使用OpenCV库将其转换为灰度图像 import cv2img cv2.imread(screenshot.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)准备模…...
Rust 使用Cargo
Rust 使用技巧 Rust 使用crates 假设你正在编写一个 Rust 程序,要使用一个名为 rand 的第三方库来生成随机数。首先,你需要在 Cargo.toml 文件中添加以下依赖项: toml [dependencies] rand "0.7.3" 然后运行 cargo build&…...
【k8s】集群搭建篇
文章目录 搭建kubernetes集群kubeadm初始化操作安装软件(master、所有node节点)Kubernetes Master初始化Kubernetes Node加入集群部署 CNI 网络插件测试 kubernetes 集群停止服务并删除原来的配置 二进制搭建(单master集群)初始化操作部署etcd集群安装Docker部署master节点解压…...
10.1select并发服务器以及客户端
服务器: #include<myhead.h>//do-while只是为了不让花括号单独存在,并不循环 #define ERR_MSG(msg) do{\fprintf(stderr,"%d:",__LINE__);\perror(msg);\ }while(0);#define PORT 8888//端口号1024-49151 #define IP "192.168.2.5…...
几个好用的测试HTTP请求的网站
Reqres (https://reqres.in):Reqres提供了一个模拟的REST API,您可以使用它来测试POST、GET、PUT等HTTP请求,并获得相应的响应结果。 JSONPlaceholder (https://jsonplaceholder.typicode.com):JSONPlaceholder是一个免费的JSON测…...
kafka简易搭建(windows环境)
1,下载 Apache Kafka 查找 kafka_2.13-3.2.1.tgz 2,java版本需要17以上 3,配置server.properties的log.dirs目录、zookeeper.properties 的dataDir目录 windows反斜杠地址 4,启动 cd D:\app\kafka_2.13-3.2.1 .\bin\window…...
毕业设计选题uniapp+springboot新闻资讯小程序源码 开题 lw 调试
💕💕作者:计算机源码社 💕💕个人简介:本人七年开发经验,擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等,大家有这一块的问题可以一起交流! 💕&…...
Linux系统编程基础:进程控制
文章目录 一.子进程的创建操作系统内核视角下的父子进程存在形式验证子进程对父进程数据的写时拷贝 二.进程等待进程非阻塞等待示例: 三.进程替换内核视角下的进程替换过程:综合利用进程控制系统接口实现简单的shell进程 进程控制主要分为三个方面,分别是:子进程的创建,进程等待…...
选择和操作元素
上一篇文档我们介绍了DOM元素和DOM的获取;其实除了获取DOM,我们也可以去替换DOM元素中的文本 document.querySelector(.message).textContent "🎉Correct Number"● 除此之外,我们可以设置那个数字部分 document.que…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
