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>
额外优化建议
- 添加空值判断:如果主菜单没有子菜单,不渲染空的
<ul>容器,避免多余DOM元素。 - 移除冗余上下文处理器:
submenu上下文处理器未被模板使用,可删除以减少不必要的数据库查询。 - 优化排序字段类型:
order字段用CharField会导致数字排序异常(如"10"排在"2"前),建议改为IntegerField。 - 预加载子菜单:获取主菜单时用
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
相关产品推荐
相关产品推荐

