检查Django模板中是否存在一个模板。

14

在Django模板中,是否有一种开箱即用的方法可以在包含模板之前检查它是否存在?也欢迎提供其他替代方案,但由于特殊情况,其中一些方案可能不起作用。

例如,下面是对一个略有不同问题的答案。这不是我要找的: 如何检查Django中是否存在模板?

4个回答

19
假设include在传递错误的模板引用时不会崩溃,那么这可能是最好的选择。您的另一种选择是创建一个模板标签,基本上执行您提到的链接中的检查。
非常基本的实现:
from django import template

register = template.Library()

@register.simple_tag
def template_exists(template_name):
    try:
        django.template.loader.get_template(template_name)
        return "Template exists"
    except template.TemplateDoesNotExist:
        return "Template doesn't exist"

在你的模板中:

{% template_exists 'someapp/sometemplate.html' %}

那个标签并不是很有用,因此您可能需要创建一个真正向上下文中添加变量的标签,然后可以在if语句或其他地方检查它。


13

在尝试只显示存在的模板时,我遇到了这个问题,最终得出了以下模板标签解决方案:

仅在存在时包含模板

将以下内容放入yourapp/templatetags/include_maybe.py文件中

from django import template
from django.template.loader_tags import do_include
from django.template.defaulttags import CommentNode
register = template.Library()

@register.tag('include_maybe')
def do_include_maybe(parser, token):
    "Source: https://dev59.com/B2025IYBdhLWcg3wGB4n#18951166"
    bits = token.split_contents()
    if len(bits) < 2:
        raise template.TemplateSyntaxError(
            "%r tag takes at least one argument: "
            "the name of the template to be included." % bits[0])

    try:
        silent_node = do_include(parser, token)
    except template.TemplateDoesNotExist:
        # Django < 1.7
        return CommentNode()

    _orig_render = silent_node.render
    def wrapped_render(*args, **kwargs):
        try:
            return _orig_render(*args, **kwargs)
        except template.TemplateDoesNotExist:
            return CommentNode()
    silent_node.render = wrapped_render
    return silent_node

在您的模板中添加{% load include_maybe %}并在代码中使用{% include_maybe "my_template_name.html" %},即可从模板中访问它。

这种方法有一个好处是可以利用现有的模板包含标签,因此您可以像使用普通的{% include %}一样传递上下文变量。

根据模板是否存在进行切换

然而,如果模板存在,我想在嵌入网站上进行一些额外的格式化。 而不是编写一个{% if_template_exists %}标签,我编写了一个过滤器,使您可以使用现有的{% if %}标签。

为此,请将以下内容放入yourapp/templatetags/include_maybe.py(或其他文件)中。

from django import template
from django.template.defaultfilters import stringfilter


register = template.Library()

@register.filter
@stringfilter
def template_exists(value):
    try:
        template.loader.get_template(value)
        return True
    except template.TemplateDoesNotExist:
        return False

然后,从您的模板中,您可以执行类似以下的操作:

{% load include_maybe %}

{% if "my_template_name"|template_exists %}
     <div>
         <h1>Notice!</h1>
         <div class="included">
             {% include_maybe "my_template_name" %}
         </div>
     </div>
{% endif %}

使用自定义过滤器的优势在于,您可以执行以下操作:

{% if "my_template_name"|template_exists and user.is_authenticated %}...{% endif %}

使用单个{% if %}标签,而不是使用多个标签。

请注意,您仍然需要使用include_maybe


1
do_include_maybe 在 Django 1.7 中停止工作(traceback)。现在似乎异常会晚些抛出,而不再是从 do_include。我已经通过尝试始终获取模板来修复它。 - blueyed
4
嘿,我刚刚给你打电话了,可能有点突然,但这是我的模板,所以请include_maybe - user764357
你必须执行“template.loader.get_template”吗?如果你只执行“return do_include(parser, token)”,那么这本身不会抛出TemplateDoesNotExist异常吗? - Lochlan
@Lochlan 可能吧!不过它实际上会执行那个模板,这可能会很慢。 - kibibu
@Lochlan 额外的调用是为了 Django 1.7 设计的,但它似乎在那里也停止工作了 - 即使使用 bits[1],这似乎更正确。我将更新代码以适应 Django 1.7 和旧版本的工作方式。 - blueyed

4

我需要在模板存在的情况下有条件地包含模板,但是我想使用一个变量来存储模板名称,就像使用常规的 {% include %} 标签一样。

以下是我在 Django 1.7 中使用的解决方案:

from django import template
from django.template.loader_tags import do_include

register = template.Library()


class TryIncludeNode(template.Node):
    """
    A Node that instantiates an IncludeNode but wraps its render() in a
    try/except in case the template doesn't exist.
    """
    def __init__(self, parser, token):
        self.include_node = do_include(parser, token)

    def render(self, context):
        try:
            return self.include_node.render(context)
        except template.TemplateDoesNotExist:
            return ''


@register.tag('try_include')
def try_include(parser, token):
    """
    Include the specified template but only if it exists.
    """
    return TryIncludeNode(parser, token)

在Django模板中,它可能会像这样使用:
{% try_include "template-that-might-not-exist.html" %}

或者,如果模板名称存在名为template_name的变量中:

{% try_include template_name %}

2
在2+中也能完美运行! - Paul Kenjora
一个使用示例(例如解析器和标记是什么)会很有用。 - Bernd Wechner
@BerndWechner parsertoken 被传递给模板标签,@register.tag('try_include') 装饰器将其注册为一个模板标签... 我会在模板中添加一个使用它的例子 :) - Beau

1

include接受变量:

{% include template_name %}

所以你可以在你的视图中进行检查。


2
如果模板不存在,那么这是否会引发TemplateDoesNotExist异常? - Chris Pratt
好的,我的意思是只有在模板存在时才传递模板变量(并在视图中捕获潜在的异常)。这样一来,你就可以轻松地将include包装在变量检查中。但当然,这并不理想,像你建议的那样使用专用标签可能更好。 - arie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接