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

Django多对多关联下Bootstrap5动态导航标签筛选异常排查

Django + Bootstrap5 动态多对多分类标签导航问题

问题场景

Item:A、B、C、D
分类:1、2、3、4

  • 分类1关联Item:A、B
  • 分类2关联Item:C、D
  • 分类3、4无关联Item,点击后应显示空内容

使用Django的多对多关联建立Item与Category的关系,尝试用Bootstrap5动态导航标签实现分类筛选,但遇到多个问题,以下是相关代码:


代码文件

models.py

class Category(models.Model):
    name = models.CharField(max_length=100)  # 补充Django必填的max_length参数

class Item(models.Model):
    name = models.CharField(max_length=100)
    category = models.ManyToManyField(Category, related_name="items")  # 添加related_name简化反向查询

views.py

def filter_item(request):
    template = 'template.html'
    cat = Category.objects.all()
    context = {'cat': cat}
    return render(request, template, context)

原template.html(存在问题版本)

<ul class="nav nav-tabs" id="myTab" role="tablist">
    {% for c in cat %}
    <li class="nav-item" role="presentation">
        <button class="nav-link {% if  c.id == '{{ c.id }}' %} active {% endif %}" id="{{ c.name|slugify }}-tab" data-bs-toggle="tab" data-bs-target="#{{ c.name|slugify }}" type="button"
            role="tab" aria-controls="{{ c.name|slugify }}" aria-selected="true">{{ c.name }}</button>
    </li>
    {% endfor %}
</ul>
<div class="tab-content" id="myTabContent">
    {% for c in cat %}
    {% for i in c.item_set.all %}
    <div class="tab-pane fade {% if i.category.id == c.id %} show active {% endif %}" id="{{ i.name|slugify }}" role="tabpanel" aria-labelledby="{{ i.name|slugify }}-tab">{{ i.title }}</div>
    {% endfor %}
    {% endfor %}
</div>

期望实现的功能

  • 支持用户添加动态分类(及动态子分类)
  • 支持用户添加Item并关联到一个或多个分类
  • 用户点击不同分类导航标签,在标签面板中查看对应关联Item
  • 分类导航支持滑动,可查看隐藏分类

当前实现的问题

  • 标签激活状态判断逻辑错误,无法正确设置初始active标签
  • 导航标签面板仅显示每个分类下的一个Item,而非全部
  • 未实现带前后控制按钮的滚动式导航

疑问

  1. 如何正确添加/移除nav-link中的active类
  2. 如何正确添加/移除tab-pane中的show和active类

解决方案

1. 修复标签激活状态逻辑

Bootstrap5的标签切换会自动处理active类的切换,只需正确设置初始激活状态:

  • 用{% if forloop.first %}判断,给第一个分类标签添加active类,同时设置aria-selected="true",其他标签设为false
  • 无需手动编写复杂的ID判断逻辑,框架会自动处理点击后的状态切换

2. 修复标签面板显示所有Item

原代码错误地为每个Item创建独立面板,正确逻辑是每个分类对应一个面板,面板内部循环显示该分类下的所有Item:

  • 利用related_name="items",通过c.items.all()反向查询分类下的所有Item
  • 为空分类添加提示文本,优化用户体验

3. 实现滚动式分类导航(带前后按钮)

添加CSS和少量JS实现滚动功能,同时保留Bootstrap标签的原生交互:

修正后的完整template.html

<style>
.tab-nav-container {
    position: relative;
    width: 100%;
}

#myTab {
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-behavior: smooth;
    -ms-overflow-style: none;
    scrollbar-width: none;
}

#myTab::-webkit-scrollbar {
    display: none;
}

.tab-scroll-btn {
    width: 40px;
    height: 40px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>

<div class="tab-nav-container d-flex align-items-center">
    <button class="btn btn-outline-secondary tab-scroll-btn" id="scroll-left" type="button">‹</button>
    <div class="nav nav-tabs flex-nowrap overflow-hidden" id="myTab" role="tablist">
        {% for c in cat %}
        <li class="nav-item" role="presentation">
            <button class="nav-link {% if forloop.first %} active {% endif %}" 
                    id="{{ c.name|slugify }}-tab" 
                    data-bs-toggle="tab" 
                    data-bs-target="#{{ c.name|slugify }}" 
                    type="button"
                    role="tab" 
                    aria-controls="{{ c.name|slugify }}" 
                    aria-selected="{% if forloop.first %}true{% else %}false{% endif %}">
                {{ c.name }}
            </button>
        </li>
        {% endfor %}
    </div>
    <button class="btn btn-outline-secondary tab-scroll-btn" id="scroll-right" type="button">›</button>
</div>

<div class="tab-content" id="myTabContent">
    {% for c in cat %}
    <div class="tab-pane fade {% if forloop.first %} show active {% endif %}" 
         id="{{ c.name|slugify }}" 
         role="tabpanel" 
         aria-labelledby="{{ c.name|slugify }}-tab">
        {% if c.items.all %}
            {% for i in c.items.all %}
                <div>{{ i.name }}</div>
            {% endfor %}
        {% else %}
            <p>暂无关联内容</p>
        {% endif %}
    </div>
    {% endfor %}
</div>

<script>
document.addEventListener('DOMContentLoaded', function() {
    const tabNav = document.getElementById('myTab');
    const scrollLeftBtn = document.getElementById('scroll-left');
    const scrollRightBtn = document.getElementById('scroll-right');

    scrollLeftBtn.addEventListener('click', function() {
        tabNav.scrollBy({ left: -200, behavior: 'smooth' });
    });

    scrollRightBtn.addEventListener('click', function() {
        tabNav.scrollBy({ left: 200, behavior: 'smooth' });
    });

    // 根据滚动状态自动显示/隐藏按钮
    function updateScrollButtons() {
        scrollLeftBtn.style.display = tabNav.scrollLeft > 0 ? 'flex' : 'none';
        scrollRightBtn.style.display = tabNav.scrollLeft + tabNav.clientWidth < tabNav.scrollWidth ? 'flex' : 'none';
    }

    updateScrollButtons();
    tabNav.addEventListener('scroll', updateScrollButtons);
});
</script>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 10:01:15