Django inclusion_tag递归调用问题求助:KeyError: 'request'
问题描述
我将导航栏模板navbar.html引入header.html,再让实际渲染的模板继承header.html,希望在导航栏菜单项上添加提示气泡,显示某一模型的待处理请求数量。反复查阅Django 4.1官方文档仍无法定位问题根源,调试发现作为inclusion_tag的函数出现递归调用:第一次执行时context中存在request键;第二次执行时该键缺失,导致报错。添加try/except块后又触发递归深度超限错误。
错误信息
File "/Users/geoffberl/git/python/family_tools_django_site/toolpool/templatetags/tool_requests.py", line 10, in get_tool_requests_count owner = context['request'].user File "/Users/geoffberl/git/python/family_tools_django_site/venv/lib/python3.10/site-packages/django/template/context.py", line 83, in __getitem__ raise KeyError(key) KeyError: 'request'
项目结构(基于Django 4.1)
- app
- main
- templates
- header.html
- home.html
- navbar.html
- templatetags
__init__.py- tag_stuff.py
- 其他常规文件(admin、url、apps等)未列出
- templates
- main
header.html
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{% block title %}Family Tools{% endblock %}</title> <!--Bootstrap CSS--> <link href="{% static '/bootstrap/css/bootstrap.css' %}" rel="stylesheet"> {% include "main/navbar.html" %} </head> <body> {% block content %} {% endblock %} <!-- Optional Javascript --> <script src="{% static '/bootstrap/js/jquery.js' %}"></script> <script src="{% static '/bootstrap/js/bootstrap.js' %}"></script> <script src="{% static 'js/jquery.bootstrap.modal.forms.min.js' %}"></script> </body> </html>
home.html
{% extends 'main/header.html' %} {% block content %} <h1>Select a tool:</h1> <form action="{% url 'toolpool:index' %}"> <input type="submit" value="Tool Pool"/> </form> {% endblock %}
navbar.html
{% load tag_stuff %} {% get_tool_requests_count %} <!--Navbar--> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a class="navbar-brand" href="{% url 'main:homepage' %}">Family Tools</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="False" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarText"> <ul class="navbar-nav mr-auto"> {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="/logout">Logout</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Welcome, {{ user.username }}</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'main:friend_requests' %}">Friend Requests</a> </li> <li class="nav-item"> <p>You have {{ tool_requests_count }} requests</p> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="/login">Login</a> </li> {% endif %} </ul> </div> </nav>
views.py
from django.shortcuts import render # Create your views here. def homepage(request): return render(request=request, template_name='main/home.html')
tool_requests.py
from django import template register = template.Library() @register.inclusion_tag('main/navbar.html', takes_context=True) def get_tool_requests_count(context): owner = context['request'].user # Ideally some model query here which would need the 'owner' obtained above in order to get count of items. item_count = 12 return {'tool_requests_count': item_count}
问题原因与解决办法
核心原因
你的inclusion_tag装饰器指定渲染模板为main/navbar.html,而navbar.html内部又调用了这个get_tool_requests_count标签,形成无限递归调用:
- 第一次渲染
header.html时,引入navbar.html触发标签执行; - 标签执行时会再次渲染
navbar.html,模板内又会调用该标签,循环往复; - 第一次调用时context包含原始请求的
request对象,但递归调用时,inclusion_tag仅传递你返回的{'tool_requests_count': item_count},没有request键,因此触发KeyError。
解决步骤
1. 拆分模板,避免递归
创建单独的模板片段用于显示请求计数(比如tool_request_count_badge.html),不要让inclusion_tag渲染整个导航栏:
<!-- templates/main/tool_request_count_badge.html --> <p>You have {{ tool_requests_count }} requests</p>
2. 修改inclusion_tag代码
更新模板标签文件,让标签渲染新的片段模板,同时增加异常处理避免未登录场景报错:
from django import template register = template.Library() @register.inclusion_tag('main/tool_request_count_badge.html', takes_context=True) def get_tool_requests_count(context): if 'request' not in context or not context['request'].user.is_authenticated: item_count = 0 else: owner = context['request'].user # 替换为你的实际模型查询逻辑 # item_count = YourModel.objects.filter(owner=owner, status='pending').count() item_count = 12 return { 'tool_requests_count': item_count, 'user': context.get('user') }
3. 调整navbar.html的标签调用位置
移除模板顶部的{% get_tool_requests_count %},将标签放到需要显示计数的位置:
{% load tag_stuff %} <!--Navbar--> <nav class="navbar navbar-expand-lg navbar-light bg-light"> <a class="navbar-brand" href="{% url 'main:homepage' %}">Family Tools</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="False" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarText"> <ul class="navbar-nav mr-auto"> {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="/logout">Logout</a> </li> <li class="nav-item"> <a class="nav-link" href="#">Welcome, {{ user.username }}</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'main:friend_requests' %}">Friend Requests</a> </li> <li class="nav-item"> {% get_tool_requests_count %} </li> {% else %} <li class="nav-item"> <a class="nav-link" href="/login">Login</a> </li> {% endif %} </ul> </div> </nav>
额外注意事项
- 确保
settings.py中启用了django.contrib.auth.context_processors.auth(默认已启用),否则模板无法直接访问user变量; - 若需在模板中直接使用
request对象,需在settings.py的TEMPLATES['OPTIONS']['context_processors']中添加django.template.context_processors.request。
内容的提问来源于stack exchange,提问作者user3620937
相关产品推荐
相关产品推荐

