在模板标签中获取模板名称(Django)

8 投票
2 回答
8360 浏览
提问于 2025-04-17 06:54

有没有办法在模板标签中获取正在解析的模板名称?我查阅了很多资料,但没有找到合适的答案,只有之前的一个帖子 在Django模板中获取模板名称

这个帖子对我帮助不大,因为答案依赖于设置中的DEBUG选项为真,而在我的情况下,这个选项不能为真。

我其实不知道从哪里开始,所以任何建议都很欢迎 :)

编辑

简单来说,我想创建一个可插拔的标签,当它被渲染时,会检查是否有一个标签对象,这个对象将作为标签的来源。

class Tag(models.Model):
    template = models.CharFIeld(max_length=50)
    name = models.CharField(max_length=100)
    plugins = models.ForeignKey(PluginBase)

如果有标签对象,就显示所有插件对象;如果没有,就根据模板标签中提供的名称和模板名称创建一个唯一的标签对象。如果无法获取模板名称,那我想我可以只根据名称来确保唯一性。这个标签整体上有点像一个占位符,熟悉django-cms的人应该能理解。

2 个回答

3

从源代码来看,虽然 Template 对象可以访问模板名称(通过 .name),但这个值并没有传递给 Parser 对象,因此模板标签无法使用这个名称。

有几种方法可以让模板本身获取模板名称(比如把它添加到上下文中),但在模板标签中却无法做到。

正如 Daniel Roseman 在评论中提到的,如果你能详细说明你想要实现的目标,可能会有更好的方法来达到你的目的。没有冒犯的意思,但这听起来像是一个 XY问题


出于学术兴趣,我简单尝试了一下,看看是否可能实现。就我所见,这是可行的,但需要修改或“猴子补丁” Django 的源代码。

注意:以下内容不是推荐的解决方案,只是暗示了实际让它工作的可能需求。不要用于生产代码。

通过修改 django.template.base.py,我们可以添加 .template_name 属性到解析器对象,使其在模板标签中可用。

  1. compile_string 添加可选参数
  2. 将模板名称作为额外属性添加到解析器
  3. 在调用 compile_string() 时传入模板名称

为了测试这个,我定义了以下标签,它简单地返回模板名称的大写形式:

from django.template.base import Node, Library
register = Library()

class TemplateNameNode(Node):
    def __init__(self, template_name):
        self.name = template_name

    def render(self, context):
        return self.name.upper()

@register.tag
def caps_template_name(parser, token):
     return TemplateNameNode(parser.template_name)

以及以下模板:

{% load mytags %}
Template name in caps: {% caps_template_name %}

./manage.py shell 中测试时,这似乎是有效的:

>>> from django.template import loader, Context
>>> t = loader.get_template("test.html")
>>> t.render(Context({}))
u'\nTemplate name in caps: TEST.HTML\n'

虽然这看起来有效,但我必须重申,手动修改 Django 源代码绝不是一个好主意,在迁移到不同版本时会遇到各种麻烦。

3

你可以考虑使用上下文处理器来实现这个功能,但我不确定它们是否能访问到模板的名称。

一个可行的方法是为你进行渲染的调用创建一个包装器。假设你现在是这样做的:

from django.shortcuts import render
def index(request):
    return render(request, 'app/index.html', { 'foo': 'bar', })

如果你为这个创建一个自己的包装器,你可以在实际渲染之前把模板名称添加到字典里:

from django.shortcuts import render
def myrender(request, template, dictionary):
    dictionary.update({'template_name': template})
    return render(request, template, dictionary)

然后在你的视图中,像下面这样修改(假设你把上面的函数保存在 myutils.py 中,并且它在你的路径上可用):

#from django.shortcuts import render <- delete this line
from myutils import myrender as render
def index(request):
    return render(request, 'app/index.html', { 'foo': 'bar', })

现在你所有的 render 调用都会更新字典,加入模板名称。在任何模板中,你只需使用 {{ template_name }} 来获取这个名称。当然,你也可以用类似的方式更新其他渲染函数,比如 render_to_response 等。

另外,import myrender as render 可能会让你以后感到困惑,因为它的名字和 Django 的函数一样……如果是这样的话,直接导入而不加 "as render",然后把所有的 render 调用替换成 myrender。我个人更喜欢这样,因为这样可以直接替代现有的渲染函数。

撰写回答