mysql django构架图_树形结构数据库表设计与django结合

本文介绍了如何在Django中利用MySQL设计树形结构数据库表,通过left和right字段实现无递归查询。内容涵盖查询子节点、计算节点层次、添加与删除节点的操作,以及如何将数据转化为JSON格式。
摘要由CSDN通过智能技术生成

为了避免对于树形结构查询时"递归"惭怍,基于Tree的谦虚遍历设计是一个无递归查询。来报错该树数据。

创建model:

class TreeMenu(models.Model):

name = models.CharField(max_length=32,verbose_name="名字")

left = models.IntegerField(verbose_name="左")

right = models.IntegerField(verbose_name="右")

class Meta:

db_table = "TreeMenu"

如下展示数据:

0fab1e1ee51c4bb04beaa87c23bd29a2.png

刚来时看这样表结构,大部分人不清楚left和right是什么鬼,如何得到的?这样表设计并没有保存父子节点继承关系,但是你去数如下的图,你会发现,你数的顺序就是这棵树进行前序遍历顺序。

f015eebd7976221f4a65176d9ac64c58.png

举个例子:这样可以得到left大于2,并且right小于9的节点都是员工信息子节点。这样通过left,right就可以找到某个节点所拥有的子节点了,但这仅仅是不够的。那么怎样增删改查呢?

比如我们需要员工信息管理节点及其子节点们:

import os

import django

from django.db.models import Q

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "asyn_test.settings")

django.setup() # 启动django项目

# 引入models.py模型模块

from api.models import TreeMenu

#查询 left大于等于2,right小于等于9

tree = TreeMenu.objects.filter(Q(left__gte=2)&Q(right__lte=9)).values()

print(tree)

"""

"""

那么如何查询某个节点的子节点个数呢,通过左,右值可以得到,子节点总数=(右值-左值-1)/2, 以员工信息为例子,它的子节点总数为(9-2-1)/2=3,并且我们为了更直观展示树的结构,我们有时需要知道树种所处的层次,我们可以这么查询,这里还是以员工信息为例:

layer = TreeMenu.objects.filter(Q(left__lte=2)&Q(right__gte=9)).count()

print(layer)

"""

2

"""

# 可以知道,它是第二层的

通过上面方式我们可以得到每一个节点所在层数,这样我们通过ORM构造一个数据结构,如下:

tree_obj = TreeMenu.objects.all()

treemenu_list = []

for term in tree_obj:

layer = TreeMenu.objects.filter(Q(left__lte=term.left)&Q(right__gte=term.right)).count()

treemenu_list.append(

{"id":term.id,"name":term.name,"left":term.left,"right":term.right,"layer":layer}

)

print(treemenu_list)

# layer 为当前节点所在层数,你会发现获得所有节点所在的层数

"""

[{

'id': 1,

'name': '首页',

'left': 1,

'right': 40,

'layer': 1

}, {

'id': 2,

'name': '员工信息',

'left': 2,

'right': 9,

'layer': 2

}, ...]

"""

我们如何获得某个节点的父节点结构,也很简单,这里拿员工信息举例:

employ = TreeMenu.objects.filter(Q(left__lte=2)&Q(right__gte=9)).values()

print(employ)

"""

"""

如果说我们想在某个节点下添加一个新的子节点,比如我在更改信息下插入一个子节点绑定邮箱.如果田间玩,此时树形结构应该如下:

f1c7719424fd45e9ea86d2aaf7fb4f76.png

代码:

edit_info_obj = TreeMenu.objects.filter(pk=3).first()

# 获取"更改信息"当前对象right值

node_point = edit_info_obj.right

# 更新 所有节点left大于等于node_point的值

TreeMenu.objects.filter(left__gte=node_point).update(left=F('left') + 2)

# 更新 所有节点right大于等于node_point的值

TreeMenu.objects.filter(right__gte=node_point).update(right=F('right') + 2)

# 插入数据 为当前 node_point,node_point+1

TreeMenu.objects.create(name="绑定邮箱",left=node_point,right=node_point+1)

删除某个节点,如果想删除某个节点,会同时删除该节点的所有子节点,而被删除的节点个数应该为:(被删除节点右侧值-被删除节点的左侧值+1)/2,而剩下节点左,右值在大于被删除节点左、右值的情况下进行调整。

这样以删除更改信息为例,那么首先找到该节点左侧值和右侧值,并且大于等于左侧值小于等于右侧值的节点都是要删除节点,也就是left=3,right=10,left<=3 and right<=10,节点都要删除,其他节点left的值大于删除节点left值应该减去(被删除节点右侧值-被删除节点的左侧值+1),其他节点right值大于删除节点right值也应该减去(被删除节点右侧值-被删除节点的左侧值+1)。如下图:

79fcb9bd35c406eb659985d99fae65da.png

代码:

edit_info_obj = TreeMenu.objects.filter(pk=3).first()

left_ponit = edit_info_obj.left#3

right_ponit = edit_info_obj.right#10

# 删除该节点下大于等于左侧值,小于等于右侧值的对象

TreeMenu.objects.filter(Q(left__gte=left_ponit)&Q(right__lte=right_ponit)).delete()

# 当其他节点左侧值大于删除节点左侧值,更新其他节点左侧值。

TreeMenu.objects.filter(left__gt=left_ponit).update(left=F('left')-(right_ponit-left_ponit+1))

# 当其他节点右侧值大于删除节点右侧值,更新其他节点右侧值。

TreeMenu.objects.filter(right__gt=right_ponit).update(right=F('right')-(right_ponit-left_ponit+1))

那么django中如何将数据封装json格式取出呢?上代码

class SchemaMenuSerializer(serializers.ModelSerializer):

layer = serializers.SerializerMethodField()

class Meta:

model = models.TreeMenu

fields = "__all__"

depth = 1

# 用于计算当前节点在第几层。

def get_layer(self,obj):

left = obj.left

right = obj.right

count_layer = models.TreeMenu.objects.filter(Q(left__lte=left) & Q(right__gte=right)).count()

return count_layer

def parse_data(layer_dict,tree_dict):

left = layer_dict.get("left")

right = layer_dict.get("right")

layer = layer_dict.get("layer")

id = layer_dict.get("id")

tree_dict["left"] = left

tree_dict["right"] = right

tree_dict["layer"] = layer

tree_dict["id"] = id

# 获取子节点数据

tree = models.TreeMenu.objects.filter(Q(left__gt=left) & Q(right__lt=right))

ser_tree = SchemaMenuSerializer(instance=tree, many=True).data

sub_ser_tree = [dict(i) for i in ser_tree if i.get("layer") == layer + 1]

# 递归创建子节点

for sub in sub_ser_tree:

tree_dict[sub.get("name")] = {}

parse_data(sub, tree_dict[sub.get("name")])

def make_node_list(ser_schemamenu):

# 构建首页字典

tree_dict = {

ser_schemamenu.get("name"):{},

"id":ser_schemamenu.get("id"),

"layer":ser_schemamenu.get("layer"),

"left":ser_schemamenu.get("left"),

"right":ser_schemamenu.get("right"),

}

# 交给parse_data,它会递归取出第二层,第三层。。。。数据

parse_data(ser_schemamenu,tree_dict[ser_schemamenu.get("name")])

return tree_dict

class SchemaMenu(APIView):

def get(self,request,*args,**kwargs):

# 这里第一层最高节点 也就是首页

schema_menu_obj = models.TreeMenu.objects.filter(pk=1).first()

# 首页字段序列化 获取layer层数

ser_schema_menu = SchemaMenuSerializer(instance=schema_menu_obj).data

# 创建节点

tree_dict = make_node_list(ser_schema_menu)

return Response({"code":200,"msg":"OK","data":tree_dict})

效果:

d59dcde44e25606070b21db1a665ea87.png

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值