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

Django从数据库取数据时子菜单显示异常问题排查

问题描述

我用Django实现从数据库拉取的动态多级菜单,已经创建了关联的Mainmenu和Submenu数据表。目前主菜单能正常加载显示,但子菜单虽已加载却堆叠重叠,无法正常下拉;但手动写死菜单代码时,主、子菜单都能正常工作。请问哪里操作有误?

效果对比

  • 预期效果:预期效果
  • 实际效果:实际效果

现有代码

Django模板代码

<div class="collapse navbar-collapse sub-menu-bar" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
        {% for item in mainmenu %}
        <li class="nav-item">
            <a href="{{ item.menulink }}">{{ item.name }}</a>
            {% for submenu in item.submenu.all %}
                <ul class="sub-menu">
                    <li>
                        <a href="{{ submenu.menulink }}">{{ submenu.name }}</a>
                    </li>
                </ul>
            {% endfor%}
        </li>
        {% endfor %}
    </ul>
</div>

自定义上下文处理器

from setup.models import Setup, Mainmenu, Submenu

def setup(request):
    setup = Setup.objects.first()
    return {'organisation': setup}

def mainmenu(request):
    mainmenu = Mainmenu.objects.order_by("order").filter(menutype="main")
    return {'mainmenu': mainmenu}

def submenu(request):
    submenu = Submenu.objects.order_by("order").filter(menutype="submenu")
    return {'submenu': submenu}

数据库表模型

class Mainmenu(models.Model):
    name = models.CharField(_("Menu Name"), max_length=256, blank=True, null=True, unique=False)
    menulink = models.CharField(_("Menu Link"), max_length=512, blank=True, null=True, help_text="Enter menu link like this, <code>https://uidc.com.ng/page/display/about-us/</code>")
    order = models.CharField(_("Menu Order"), max_length=512, blank=True, null=True)
    is_active = models.CharField(_("Is Active"), max_length=256, blank=True, null=True)
    menutype = models.CharField(_("Menu Type"), max_length=256)
    slug = models.SlugField(_("Slug"), max_length=256, blank=True, null=True, unique=False)
    datecreated = models.DateField(_("Date Created"), auto_now=True)

    class Meta:
        verbose_name = _("Mainmenu")
        verbose_name_plural = _("Mainmenus")

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super().save(*args, **kwargs)

class Submenu(models.Model):
    name = models.CharField(_("Submenu Name"), max_length=256, blank=True, null=True)
    mainmenu = models.ForeignKey(
        Mainmenu, verbose_name=_("Main Menu"), on_delete=models.CASCADE, related_name="submenu"
    )
    menulink = models.CharField(_("Submenu Link"), max_length=512, blank=True, null=True)
    order = models.CharField(_("Submenu Order"), max_length=512, blank=True, null=True)
    is_active = models.CharField(_("Is Active"), max_length=256, blank=True, null=True)
    menutype = models.CharField(_("Submenu Type"), max_length=256, blank=True, null=True)
    slug = models.SlugField(_("Slug"), max_length=256, blank=True, null=True, unique=True)
    datecreated = models.DateField(_("Date Created"), auto_now=True)

    class Meta:
        verbose_name = _("Submenu")
        verbose_name_plural = _("Submenus")

问题原因及修复方案

核心错误:模板中子菜单循环结构错误

你当前的模板代码里,每个子菜单都生成了独立的<ul class="sub-menu">容器,而非将同一主菜单下的所有子菜单放在同一个<ul>内。这导致每个子菜单单独占用一个下拉容器,最终出现堆叠重叠的问题。而手动写死代码时,你肯定是把同一主菜单的子菜单都放在一个<ul>里,所以显示正常。

修复后的模板代码

将<ul class="sub-menu">移到子菜单循环的外部,确保同一主菜单的所有子菜单都处于同一个下拉列表容器中:

<div class="collapse navbar-collapse sub-menu-bar" id="navbarSupportedContent">
    <ul class="navbar-nav mr-auto">
        {% for item in mainmenu %}
        <li class="nav-item">
            <a href="{{ item.menulink }}">{{ item.name }}</a>
            {% if item.submenu.all %}
                <ul class="sub-menu">
                    {% for submenu in item.submenu.all %}
                        <li>
                            <a href="{{ submenu.menulink }}">{{ submenu.name }}</a>
                        </li>
                    {% endfor%}
                </ul>
            {% endif %}
        </li>
        {% endfor %}
    </ul>
</div>

额外优化建议

  1. 添加空值判断:如果主菜单没有子菜单,不渲染空的<ul>容器,避免多余DOM元素。
  2. 移除冗余上下文处理器:submenu上下文处理器未被模板使用,可删除以减少不必要的数据库查询。
  3. 优化排序字段类型:order字段用CharField会导致数字排序异常(如"10"排在"2"前),建议改为IntegerField。
  4. 预加载子菜单:获取主菜单时用prefetch_related预加载关联子菜单,解决N+1查询问题:
def mainmenu(request):
    mainmenu = Mainmenu.objects.order_by("order").filter(menutype="main").prefetch_related('submenu')
    return {'mainmenu': mainmenu}

内容的提问来源于stack exchange,提问作者Mukoro Godwin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 02:00:37