django读取菜单树表生成JSON

个人学习开发一个django web端程序,基础不扎实期望解决以下问题:

将数据库中读取的菜单结构,生成前端jsTree所需的json格式

表结构如下,目前设计的最深4级:
menu_id: 使用此ID进行同级排序,用于左侧菜单树的顺序展示
menu_name:菜单树显示名称
menu_parent_id:该节点的上级ID,ID用的数据库默认主键,非menu_id,因为menu_id可能会因为调整顺序而变更

img

期望输出返回前端的JSON格式:

[
  {
    "id": "1",
    "text": "安全功能三级",
    "children": [
      {
        "id": "1.1",
        "text": "子节点 1.1",
        "children": [
          { "id": "1.1.1", "text": "叶子节点 1.1.1" },
          { "id": "1.1.2", "text": "叶子节点 1.1.2" }
        ]
      },
      { "id": "1.2", "text": "子节点 1.2" }
    ]
  },
  {
    "id": "2",
    "text": "节点 2",
    "children": [
      { "id": "2.1", "text": "子节点 2.1" },
      { "id": "2.2", "text": "子节点 2.2" }
    ]
  }
]

以下是一些Django环境信息:
数据库表models信息:

class casetree(models.Model):
    menu_id = models.IntegerField(verbose_name='菜单ID')
    menu_name = models.CharField(verbose_name='菜单名称',max_length=32)
    menu_parent_id = models.IntegerField(verbose_name='上级ID')

insert into case_casetree (menu_id,menu_name,menu_parent_id) values (23,'一级菜单A',0);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (24,'一级菜单B',0);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (25,'一级菜单C',0);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (26,'二级菜单AA',12);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (27,'二级菜单AB',12);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (28,'三级菜单AAA',18);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (29,'四级菜单AAAA',20);
insert into case_casetree (menu_id,menu_name,menu_parent_id) values (30,'四级菜单AAAB',20);

期望可以得到代码片段或解决的具体思路,自己研究了下递归没鼓捣出来

引用chatgpt部分指引作答:
你可以使用递归的方式来生成所需的JSON格式。首先,你需要查询数据库获取所有的菜单数据,并以某种方式对其进行排序,确保同级菜单按照menu_id的顺序展示。然后,你可以定义一个递归函数,该函数接收当前菜单节点的ID作为参数,并返回该节点及其子节点的JSON表示。

下面是一个示例代码片段,展示了如何实现这个逻辑:

def generate_menu_json(menu_id):
    menu_data = casetree.objects.filter(menu_parent_id=menu_id).order_by('menu_id')
    menu_json = []

    for menu in menu_data:
        menu_item = {
            'id': str(menu.menu_id),
            'text': menu.menu_name,
        }

        children = generate_menu_json(menu.menu_id)
        if children:
            menu_item['children'] = children

        menu_json.append(menu_item)

    return menu_json


# 在视图或其他地方调用递归函数,并将生成的JSON数据返回给前端
def get_menu_json(request):
    root_menu_id = 0  # 假设根节点的menu_id为0
    menu_json = generate_menu_json(root_menu_id)
    return JsonResponse(menu_json, safe=False)

这个例子中,首先从数据库中获取指定menu_id的所有子菜单,并按照menu_id排序。然后,对每个菜单项进行处理,创建一个字典表示该菜单节点,包含'id'和'text'属性。接下来,递归调用generate_menu_json函数来获取当前菜单节点的子节点,并将其作为'children'属性添加到菜单项字典中。最后,将菜单项字典添加到menu_json列表中。

最后,你可以将生成的menu_json作为JSON数据返回给前端。这里使用JsonResponse来生成JSON响应。

请注意,上述代码是基于你提供的表结构和数据示例进行的,并假设根节点的menu_id为0。你可能需要根据实际情况对代码进行适当的调整。另外,确保在Django的视图中导入必要的模块,如JsonResponse和你的casetree模型。


from django.http import JsonResponse
from .models import Menu

def get_menu(menu_id):
    menu = Menu.objects.get(menu_id=menu_id)
    menu_data = {
        'menu_id': menu.menu_id,
        'menu_name': menu.menu_name,
        'sort_order': menu.sort_order,
        'children': []
    }
    child_menus = Menu.objects.filter(parent_id=menu_id).order_by('sort_order')
    for child_menu in child_menus:
        menu_data['children'].append(get_menu(child_menu.menu_id))
    return menu_data

def menu_data(request):
    top_menus = Menu.objects.filter(parent_id=0).order_by('sort_order')
    menu_data = []
    for top_menu in top_menus:
        menu_data.append(get_menu(top_menu.menu_id))
    return JsonResponse(menu_data, safe=False)

在上述代码中,我们首先定义了一个递归函数get_menu,该函数接收一个菜单ID作为参数。函数根据菜单ID获取菜单对象,并构建一个菜单字典。然后,它查找该菜单的子菜单,并对每个子菜单调用递归函数,将子菜单添加到父菜单的children字段中。

在menu_data视图函数中,我们首先获取顶级菜单(父菜单ID为0的菜单),然后对每个顶级菜单调用get_menu函数,得到包含层级结构的菜单数据列表。最后,我们使用JsonResponse返回菜单数据。

请注意,在调用JsonResponse时,我们将safe参数设置为False,以允许返回的JSON对象是一个列表。

这样修改后,递归函数会根据父菜单ID来确定哪个菜单的子菜单,并以层级结构的方式输出所有菜单数据。


def category_data(request):
    category = CategoryData.objects.all()  // 数据库数据
    category_info = serializers.serialize('json', category) // 将数据转为json格式
    category_info = json.loads(category_info)
    category_info_data = []

    for i in range(len(category_info)):
        category_info_data.append(category_info[i]['fields'])

    lists = []
    tree = {}
    pid = ''
    for i in category_info_data:
        item = i
        tree[item['cat_id']] = item
    root = None
    for i in category_info_data:
        obj = i
        if not obj['pid']:
            root = tree[obj['cat_id']]
            lists.append(root)
        else:
            pid = obj['pid']
            if 'childList' not in tree[pid]:
                tree[pid]['childList'] = []
            tree[pid]['childList'].append(tree[obj['cat_id']])

    data = {
        'data': {
            'category_data': lists
        },
        'code': 200,
        'msg': 'true'
    }
    return JsonResponse(data, safe=False)

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 以下回答来自chatgpt:

    解决方案:

    首先需要定义一个递归函数来获取菜单树。

    如果有子节点,就遍历子节点,将子节点作为该节点的子节点,不断递归下去,直至获取到所有的节点。

    下面是获取菜单树的函数代码:

    from django.core import serializers
    from django.http import JsonResponse
    
    def get_menu_tree(menu_list, parent_id=None):
        result = []
        for menu in menu_list:
            if menu['menu_parent_id'] == parent_id:
                children = get_menu_tree(menu_list, menu['menu_id'])
                menu_dict = {
                    'id': menu['menu_id'],
                    'text': menu['menu_name'],
                    'children': children
                }
                result.append(menu_dict)
        return result
    

    接下来是将菜单树转换为jsTree需要的JSON格式。

    在view中获取数据库中的菜单数据并调用get_menu_tree函数,然后将结果转换为JSON格式。

    最后将JSON数据返回到前端即可。

    下面是将菜单树转换为jsTree格式的代码:

    def menu_tree(request):
        menu_list = list(Menu.objects.order_by('menu_id').values('menu_id', 'menu_name', 'menu_parent_id'))
        menu_tree = get_menu_tree(menu_list)
        menu_json = serializers.serialize('json', menu_tree)
        return JsonResponse(menu_json, safe=False)
    

    在前端中调用该接口获取JSON数据,并将数据转换为jsTree格式后,即可完成菜单的呈现。

    希望以上解决方案能够帮到你。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

直接按parent_id分组查询