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

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等)未列出

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 %}
{% 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标签,形成无限递归调用:

  1. 第一次渲染header.html时,引入navbar.html触发标签执行;
  2. 标签执行时会再次渲染navbar.html,模板内又会调用该标签,循环往复;
  3. 第一次调用时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:45:47