Django问题:GET请求后页面重载导致Bootstrap折叠按钮状态重置
解决Bootstrap折叠按钮与表单提交页面重载的冲突问题
问题分析
当前代码里,按钮同时承担了表单提交(type="submit")和Bootstrap折叠触发器的角色,点击时会先触发折叠的JS逻辑,随即提交表单导致页面重载——前端维护的折叠状态会被重置,同时后端返回的grades数据会被所有科目折叠框复用,无法实现每个科目独立展示自身成绩的需求。
解决方案
方案一:AJAX异步加载成绩(推荐)
通过异步请求获取成绩,无需页面重载,彻底避免折叠状态丢失,还能实现每个科目独立加载数据。
1. 新增后端API接口
在views.py中添加处理AJAX请求的视图:
from django.http import JsonResponse def get_subject_grades(request): if request.user.is_authenticated and request.method == 'GET': subject = request.GET.get('subject', '') if subject: # 假设get_grades返回可序列化的成绩列表,若为对象需转换为字符串 grades = Test.get_grades(student=request.user.id, subject=subject, self=Test) return JsonResponse({'grades': [str(g) for g in grades]}) return JsonResponse({'grades': []}) return JsonResponse({'error': '未登录'}, status=401)
在urls.py中配置路由:
path('get-grades/', views.get_subject_grades, name='get_subject_grades'),
2. 修改前端模板,移除表单改用AJAX
调整noten.html,去掉表单,将按钮改为普通按钮,添加JS逻辑处理点击事件:
{% if subject_list %} <ul> {% for subject in subject_list %} <p> <button class="btn btn-primary" type="button" data-bs-toggle="collapse" data-bs-target="#{{ subject }}" aria-expanded="false" aria-controls="{{ subject }}" data-subject="{{ subject }}"> {{ subject }} </button> </p> <div class="collapse" id="{{ subject }}"> <div class="card card-body" id="grades-container-{{ subject }}"> <p>加载中...</p> </div> </div> {% endfor %} </ul> <script> // 绑定所有科目按钮的点击事件 document.querySelectorAll('.btn-primary[data-subject]').forEach(button => { button.addEventListener('click', function() { const subject = this.dataset.subject; const container = document.getElementById(`grades-container-${subject}`); // 避免重复加载 if (container.dataset.loaded === 'true') return; // 发送异步请求 fetch(`{% url 'get_subject_grades' %}?subject=${encodeURIComponent(subject)}`) .then(res => res.json()) .then(data => { if (data.error) { container.innerHTML = '<p>获取成绩失败,请重新登录</p>'; return; } if (data.grades.length > 0) { let html = '<ul>'; data.grades.forEach(grade => html += `<li>${grade}</li>`); html += '</ul>'; container.innerHTML = html; } else { container.innerHTML = '<p>Du hast keine Noten in diesem Fach!</p>'; } container.dataset.loaded = 'true'; }) .catch(err => { container.innerHTML = '<p>加载成绩时出错</p>'; console.error(err); }); }); }); </script> {% endif %}
方案二:表单提交后恢复折叠状态
如果不想用AJAX,可通过URL参数标记当前展开的科目,后端渲染时给对应折叠框添加show类,重载后保持展开状态。
1. 修改后端视图,传递当前选中科目
更新views.py的noten函数:
def noten(request): if request.user.is_authenticated: object_list = Subject.objects.order_by('name') subject_list = [sub.name for sub in object_list] current_subject = request.GET.get('subject', '') grades = [] if current_subject: grades = Test.get_grades(student=request.user.id, subject=current_subject, self=Test) return render(request, 'noten.html', { 'subject_list': subject_list, 'grades': grades, 'current_subject': current_subject }) else: return redirect('loginForm:login')
2. 调整前端模板,根据参数设置折叠状态
修改noten.html:
{% if subject_list %} <form name="subject-list" method="GET"> <ul> {% for subject in subject_list %} <p> <button class="btn btn-primary" type="submit" data-bs-toggle="collapse" data-bs-target="#{{ subject }}" aria-expanded="{{ subject == current_subject|lower }}" aria-controls="{{ subject }}" value="{{ subject }}" name="subject"> {{ subject }} </button> </p> <div class="collapse {% if subject == current_subject %}show{% endif %}" id="{{ subject }}"> <div class="card card-body"> {% if subject == current_subject %} {% if grades %} {% for grade in grades %} <li>{{ grade }}</li> {% endfor %} {% else %} <p>Du hast keine Noten in diesem Fach!</p> {% endif %} {% else %} <p>点击按钮查看成绩</p> {% endif %} </div> </div> {% endfor %} </ul> </form> {% endif %}
注意:此方案仍会触发页面重载,且同一时间只能显示一个科目的成绩。
额外优化建议
- 方案一中可添加防抖逻辑,防止用户短时间内重复点击
- 方案二中给按钮添加
autocomplete="off",避免浏览器自动填充干扰 - 检查
Test.get_grades方法的调用方式,self=Test写法不符合常规类方法调用,建议改为Test.get_grades(student=request.user.id, subject=subject)(若get_grades是类方法)
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

