Django模板继承base_planner.html后侧边栏内容丢失求助
我有一个包含导航栏(navbar)和侧边栏(sidebar)的base_planner.html模板。其他模板继承该基础模板时,导航栏能正常继承,但侧边栏内容不显示,且无任何报错。唯一能正常显示侧边栏内容的是首页——仅包含一行代码{% extends 'app_planner\base_planner.html' %}的ListView页面projectsmodel_list.html。
代码文件
views.py(希望TestView能继承侧边栏内容)
class ProjectListView(LoginRequiredMixin, generic.edit.FormMixin, generic.ListView): context_object_name = 'project_list' model = ProjectsModel form_class = ProjectsForm success_url = reverse_lazy('app_planner:planner_home') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['create_project_form'] = ProjectsForm() return context def form_valid(self, form): project_name = form.cleaned_data['project_name'] project_author = self.request.user create_project_form_item = ProjectsModel(project_name=project_name, project_author=project_author) create_project_form_item.save() return super().form_valid(form) def form_invalid(self, form): return super().form_invalid(form) def post(self, request, *args, **kwargs): self.object_list = self.get_queryset() form = self.get_form() if form.is_valid(): return self.form_valid(form) else: return self.form_invalid(form) class TestView(generic.TemplateView): template_name = 'app_planner/test.html'
urls.py
app_name = 'app_planner' urlpatterns = [ path('', views.ProjectListView.as_view(), name='planner_home'), path('test/', views.TestView.as_view(), name='test'), ]
base_planner.html(body部分)
{% if user.is_authenticated %} <nav id='navbar' class="navbar navbar-expand-lg bg-white"> <div class="container-fluid"> <a id='logo' class="navbar-brand" href="{% url 'app_planner:planner_home' %}"><h3><b><span style="color:#FC5E5E;">Savvy</span> Planner</b></h3></a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarNavAltMarkup"> <div class="navbar-nav"> <div class="navbar-center"> <a id='projects_board' class="nav-link {% if request.resolver_match.view_name == 'app_planner:planner_home' %}active{% endif %}" href="{% url 'app_planner:planner_home' %}">Projects & Tasks</a> <a id='analytics' class="nav-link {% if request.resolver_match.view_name == 'app_planner:analytics' %}active{% endif %}" href="#">Analytics</a> <a id='analytics' class="nav-link {% if request.resolver_match.view_name == 'app_planner:test' %}active{% endif %}" href="{% url 'app_planner:test' %}">Test</a> </div> <div class="navbar-right"> <a id='notifications' class="nav-link" href="#"><img src="{% static 'notifications_empty.svg' %}"></a> <a id='logout' class="nav-link" href="{% url 'app_planner:logout' %}" class="btn btn-simple"><img src="{% static 'logout.svg' %}" width="20" height="20"></a> </div> </div> </div> </div> </nav> <div class="sidebar"> <div class='row' style="font-family: 'Nunito', sans-serif; padding-bottom: 25px;"> <div class='col-sm-6' style="height: 60px; width: 60px; border-radius: 16px; background-color: #FC5E5E;"></div> <div class="col-sm-6" style="width: fit-content;"> <p><span style='font-size:25px'><b>Awesome Team</span><br> <span style='color:#888B94'>Workspace</span></b></p> </div> </div> <div class='sidebar_projects' style="border-top: solid 2px #E3E4E9; margin-left: -10px;"> <br> <form method="POST"> {% csrf_token %} <div class='row project_form'> {% for field in create_project_form %} <div class='col-sm-4' style='align-items:center; display: flex;'>{{ field.label }}</div> <div class='col-sm-4'>{{ field }}</div> <div class='col-sm-4' style='align-items:center; display: flex;'><button id='add_project' type="submit" style='background:white;border:0px;'><img src="{% static 'plus_button.svg' %}"></button></div> {% endfor %} </div> </form> <div class="row"> {% for project in project_list %} <div class='col-sm-12 single_project'><a href="{% url 'app_planner:project_detail' pk=project.pk %}"><span style='color:rgba(255, 255, 255, 0);'>__</span>{{ project.project_name }}</a></div> {% endfor %} </div> </div> </div> <div class='content'> {% block content %} {% endblock %} </div> {% endif %}
test.html
{% extends 'app_planner/base_planner.html' %} {% block content %} <h1>Check the sidebar</h1> {% endblock %}
我试过修改模板、把ProjectListView的内容复制到其他视图、单独创建sidebar.html用{% include %}引入,都没用,侧边栏还是空的。这是第一次遇到{% extend %}相关的异常,找不到问题原因,求帮助。
侧边栏内容不显示的核心原因是TestView没有向模板传递侧边栏所需的上下文数据:
- 侧边栏里用到了
create_project_form和project_list这两个变量,这两个变量是ProjectListView通过get_context_data和ListView自带的查询集逻辑提供的 - 而
TestView是普通的TemplateView,默认不会传递这两个变量,所以模板里的{% for field in create_project_form %}和{% for project in project_list %}循环都没有数据,看起来侧边栏就空了
解决方法有两种,选适合你的:
方法1:让TestView继承ListView并复用上下文逻辑
把TestView改成继承ListView,同时复用ProjectListView的上下文方法,这样就能自动获取project_list和表单:
class TestView(LoginRequiredMixin, generic.ListView): template_name = 'app_planner/test.html' model = ProjectsModel context_object_name = 'project_list' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['create_project_form'] = ProjectsForm() return context
(LoginRequiredMixin要加上,确保用户已登录,和ProjectListView保持一致)
方法2:给TestView手动添加上下文数据
如果你不想让TestView继承ListView,就在TestView里重写get_context_data方法,手动传入需要的变量:
class TestView(LoginRequiredMixin, generic.TemplateView): template_name = 'app_planner/test.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # 传入项目列表,过滤当前用户的项目(和ProjectListView逻辑一致) context['project_list'] = ProjectsModel.objects.filter(project_author=self.request.user) # 传入创建项目的表单 context['create_project_form'] = ProjectsForm() return context
额外优化建议
为了避免重复代码,你可以把获取侧边栏上下文的逻辑抽成一个混合类(Mixin),让所有需要侧边栏的视图继承它:
class SidebarContextMixin: def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['create_project_form'] = ProjectsForm() context['project_list'] = ProjectsModel.objects.filter(project_author=self.request.user) return context # 然后你的视图可以这样写: class ProjectListView(LoginRequiredMixin, SidebarContextMixin, generic.edit.FormMixin, generic.ListView): # 原有的代码,去掉重复的get_context_data context_object_name = 'project_list' model = ProjectsModel form_class = ProjectsForm success_url = reverse_lazy('app_planner:planner_home') # ... 其他方法保留,去掉原来的get_context_data ... class TestView(LoginRequiredMixin, SidebarContextMixin, generic.TemplateView): template_name = 'app_planner/test.html'
这样所有继承SidebarContextMixin的视图都会自动拥有侧边栏所需的上下文,不用重复写代码。
内容的提问来源于stack exchange,提问作者Анатолий Григорович

