You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django递归生成嵌套菜单时避免重复SQL查询?

解决Django嵌套菜单重复SQL查询的优化方案

1. 一次性加载全量菜单数据,内存中构建树形结构

递归查询数据库是重复SQL的核心诱因,应先拉取所有菜单数据到内存,再用Python构建父子关系映射,彻底避免每次递归触发数据库请求。

修改MenuView视图

from django.views.generic import TemplateView
from .models import Menu

class MenuView(TemplateView):
    template_name = 'menu.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 一次性加载所有菜单,用select_related预加载parent外键关联数据
        all_menus = Menu.objects.all().select_related('parent').order_by('parent_id', 'order')
        # 构建父节点ID到子菜单列表的映射字典
        menu_tree = {}
        for menu in all_menus:
            parent_key = menu.parent_id if menu.parent_id is not None else 0
            if parent_key not in menu_tree:
                menu_tree[parent_key] = []
            menu_tree[parent_key].append(menu)
        context['menu_tree'] = menu_tree
        return context

2. 调整模板标签,基于内存树递归渲染

移除模板标签中的数据库查询逻辑,直接从内存映射中获取子菜单:

from django import template

register = template.Library()

@register.inclusion_tag('menu_item.html')
def draw_menu(menu_tree, parent_key=0):
    # 从内存字典中直接取当前父节点的子菜单
    children = menu_tree.get(parent_key, [])
    return {'children': children, 'menu_tree': menu_tree}

3. 适配模板渲染逻辑

子菜单模板(menu_item.html)

<ul>
{% for item in children %}
    <li>
        <a href="{{ item.url }}">{{ item.name }}</a>
        <!-- 递归渲染子菜单,直接传入内存树结构 -->
        {% if item.id in menu_tree %}
            {% draw_menu menu_tree parent_key=item.id %}
        {% endif %}
    </li>
{% endfor %}
</ul>

主模板(menu.html)

{% load menu_tags %}
<!-- 渲染根菜单(parent_key设为0,对应模型中parent为null的根节点) -->
{% draw_menu menu_tree %}

4. 模型层规范优化(可选)

确保Menu模型的外键关联定义清晰,方便后续扩展:

from django.db import models

class Menu(models.Model):
    name = models.CharField(max_length=100)
    url = models.CharField(max_length=200)
    parent = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE, related_name='children')
    order = models.IntegerField(default=0)

    def __str__(self):
        return self.name

核心优化逻辑

通过select_related预加载外键关联数据,一次性拉取所有菜单记录;再用Python构建父子节点的内存映射,后续递归渲染时直接从字典中读取数据,彻底消除重复SQL查询。

内容的提问来源于stack exchange,提问作者Daniil Filippov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 13:52:37