如何在 django 模板中重复“块”

我想在相同的 django 模板中使用相同的 {% block% }两次。我希望这个代码块在我的基本模板中出现不止一次:

# base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
<h1>{% block title %}My Cool Website{% endblock %}</h1>
</body>
</html>

然后延伸:

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}


# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}


# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

我将得到一个异常,因为 Django 希望该块只出现一次:

TemplateSyntaxError/

出现名称为“ title”的“ block”标记 不止一次

一个快速而肮脏的解决方案是将代码块 书名复制到 题目1题目2:

# blog.html
{% extends 'base.html' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}

但这违反了 干的原则。这将是非常困难的,因为我有很多继承模板,也因为我不想去地狱; -)

这个问题有什么诀窍或解决办法吗?如何在不复制所有代码的情况下在模板中重复相同的代码块?

50558 次浏览

实际上你可能并不想使用一个块,而只是使用一个变量:

# base.html
<html>
<head>
<title>\{\{ title|default:"My Cool Website" }}</title>
</head>
<body>
<h1>\{\{ title|default:"My Cool Website" }}</h1>
</body>
</html>

然后通过上下文设置标题。

解决这个问题有两个简单的办法。

最简单的方法是将标题放入上下文变量中。您可以在视图中设置上下文变量。

如果您正在使用类似通用视图的东西,而且对于图片、猫等没有 views.py,那么您可以采用 在上下文中设置变量的自定义模板标记的方式。

按照这个路线,你可以这样做:

{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...

然后在你的 base.html:

...
{% block title %}\{\{ page_title }}{% endblock %}
...
<h1>\{\{ page_title }}</h1>

你可以使用 {% include subtemplate.html %}不止一次。它不同于块,但做的诀窍。

我认为在这种情况下使用上下文处理器是一种过度消耗,你可以很容易地这样做:

#base.html
<html>
<head>
<title>{% block title %}My Cool Website{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>

然后:

# blog.html
{% extends 'base.html' %}
{% block content %}
<h1>{% block title %}My Blog{% endblock %}</h1>
Lorem ipsum here...
{% endblock %}

等等,看起来像是干燥兼容的。

使用 Django 模板宏插件:

Https://gist.github.com/1715202 (django > = 1.4)

或者

Http://www.djangosnippets.org/snippets/363/ (django < 1.4)

Django > = 1.4

# base.html
{% kwacro title %}
{% block title %}My Cool Website{% endblock %}
{% endkwacro %}


<html>
<head>
<title>{% usekwacro title %}</title>
</head>
<body>
<h1>{% usekwacro title %}</h1>
</body>
</html>

还有

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

Django < 1.4

# base.html
{% macro title %}
{% block title %}My Cool Website{% endblock %}
{% endmacro %}


<html>
<head>
<title>{% usemacro title %}</title>
</head>
<body>
<h1>{% usemacro title %}</h1>
</body>
</html>

还有

# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}

下面是我自己尝试做同样事情时发现的一种方法:

# base_helper.html
<html>
<head>
<title>{% block _title1 %}{% endblock %}</title>
</head>
<body>
<h1>{% block _title2 %}{% endblock %}</h1>
</body>
</html>




# base.html
{% extends "base_helper.html" %}


# Copy title into _title1 & _title2, using "My Cool Website" as a default.
{% block _title1 %}{% block _title2 %}{% block title %}My Cool Website{% endblock %}{% endblock %}{% endblock %}

不幸的是,需要额外的文件,但不需要您从视图中传递标题。

根据 Van Gale 的建议,您可以通过向 templatetags.py 文件添加以下内容来创建 get 和 set 标记:

register = template.Library()


Stateful = {}
def do_set(parser, token):
_, key = token.split_contents()
nodelist = parser.parse(('endset',))
parser.delete_first_token()  # from the example -- why?
return SetStatefulNode(key,nodelist)


class SetStatefulNode(template.Node):
def __init__(self, key, nodes):
Stateful[key] = nodes
def render(self, context):
return ''
register.tag('set', do_set)


def do_get(parser, token):
tag_name, key = token.split_contents()
return GetStatefulNode(key)


class GetStatefulNode(template.Node):
def __init__(self, key):
self.key = key
def render(self, context):
return ''.join( [x.render(context) for x in Stateful[self.key]] )


register.tag('get', do_get)

然后通过 {% set foo %}put data here{% endset %}在一个模板中设置值,并在另一个模板中通过 {% get foo %}获取值。

这里有一些讨论: Http://code.djangoproject.com/ticket/4529 很明显,django 核心团队拒绝了这个提议,因为他们认为这不是一个常用的场景,但是我不同意。

Repeat 块是一个简单而干净的实现: Https://github.com/smileychris/django-repeatblock

模板宏是另外一个,但是作者提到它没有经过仔细的测试: Http://www.djangosnippets.org/snippets/363/

我用了重复屏蔽。

我也遇到过同样的需要,在我的模板文件中重复{% block% }。问题在于,我希望 Django {% block% }可以用于 Django 条件的任何一种情况,而且我希望{% block% }可以被可能扩展当前文件的后续文件覆盖。(所以在这个例子中,我想要的绝对是一个块而不是一个变量,因为我没有在技术上重用它,它只是出现在一个条件的两端。

问题:

下面的 Django 模板代码会导致一个模板语法错误,但是我认为它是一个有效的“想”在一个条件中重用一个已定义的{% block% }(IE,为什么 Django 解析器在一个条件的两端都验证语法,难道它不应该只验证 TRUTHY 条件吗

# This example shows a \{\{ DEBUG }} conditional that loads
#   Uncompressed JavaScript files if TRUE
#   and loads Asynchronous minified JavaScript files if FALSE.


# BASE.html
{% if DEBUG %}
<script src="\{\{MEDIA_URL}}js/flatfile.1.js"></script>
<script src="\{\{MEDIA_URL}}js/flatfile.2.js"></script>
<script src="\{\{MEDIA_URL}}js/flatfile.3.js"></script>
<script type="text/javascript">
{% block page_js %}
var page = new $site.Page();
{% endblock page_js %}
</script>
{% else %}
<script type="text/javascript">
// load in the PRODUCTION VERSION of the site
// minified and asynchronosly loaded
yepnope([
{
load : '{MEDIA_URL}}js/flatfiles.min.js',
wait : true,
complete : function() {
{% block page_js %} // NOTE THE PAGE_JS BLOCK
var page = new $site.Page();
{% endblock page_js %}
}
}
)];
</script>
{% endif %}


# ABOUT.html
{% extends 'pages/base.html' %}
{% block page_js %}
var page = new $site.Page.About();
{% endblock page_js %}

解决方案:

可以使用{% include% }多次有条件地插入{% 块% }。这对我很有用,因为 Django 语法检查器只包含 TRUTHY {% include% }。请参阅以下结果:

# partials/page.js
{% block page_js %}
var page = new $site.Page();
{% endblock %}


# base.html
{% if DEBUG %}
<script src="\{\{MEDIA_URL}}js/flatfile.1.js"></script>
<script src="\{\{MEDIA_URL}}js/flatfile.2.js"></script>
<script src="\{\{MEDIA_URL}}js/flatfile.3.js"></script>
<script type="text/javascript">
{% include 'partials/page_js.html' %}
</script>
{% else %}
<script type="text/javascript">
yepnope([
{
load : '{MEDIA_URL}}js/flatfiles.min.js',
wait : true,
complete : function() {
{% include 'partials/page_js.html' %}
}
}
)];
</script>
{% endif %}

小树枝中,你可以这样做:

# base.html
<html>
<head>
<title>\{\{ block('title') }}</title>
</head>
<body>
<h1>\{\{ block('title') }}</h1>
</body>
</html>


# blog.html
{% extends 'base.html' %}
{% block title %}My Blog{% endblock %}


# pictures.html
{% extends 'base.html' %}
{% block title %}My Pictures{% endblock %}


# cats.html
{% extends 'base.html' %}
{% block title %}My Cats{% endblock %}

对于任何遇到这种情况的人来说,作为一个更新,我采用了上面提到的代码片段,并将其转换为一个模板标记库 django-Macros,这使得宏更加强大,并且显式地实现了一个重复的块模式: 姜戈宏

下面是一个类似于上面的 do_setdo_get模板标签答案的轻量级解决方案。Django 允许您将整个模板上下文传递到一个标记中,该标记允许您定义一个全局变量。

Html:

<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<title>\{\{ title }}</title>
{% endblock %}
</head>
<body>
<h1>\{\{ title }}</h1>
</body>
</html>

Html:

{% extends "base.html" %}


{% block head %}
{% define 'title' 'Homepage | title' %}
\{\{ block.super }}
{% endblock %}

自定义标记(在这里得到的想法: https://stackoverflow.com/a/33564990/2747924) :

@register.simple_tag(takes_context=True)
def define(context, key, value):
context.dicts[0][key] = value
return ''

也不要忘记 {% load %}您的自定义标记或将它们添加到模板选项 内在的列表,这样您就不必在每个模板中加载它们。这种方法的唯一限制是必须从一个块标记中调用 {% define %},因为子模板只呈现与父标记匹配的块标记。不知道有没有别的办法。还要确保 define调用在您尝试使用它之前出现。

我用 这个答案保持干燥。

{% extends "base.html" %}


{% with "Entry Title" as title %}
{% block title %}\{\{ title }}{% endblock %}
{% block h1 %}\{\{ title }}{% endblock %}
{% endwith %}

所选择的答案暗示了一个简单的解决方案,可以在子模板中将一个标记包装到另一个标记中,从而为它们提供相同的值。我用这个来做社交图片。

儿童模板:

{% extends 'base.html' %}
...
{% block meta_image %}
{% block meta_image_secure %}
{% if object.cover_pic %}
\{\{ object.cover_pic.url }}
{% else %}
https://live-static.welovemicro.com/static/img/device-dark.png
{% endif %}
{% endblock %}
{% endblock %}
...

然后在母 base.html中:

...
<meta property="og:image" itemprop="image" content="{% block meta_image %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
<meta property="og:image:secure_url" itemprop="image" content="{% block meta_image_secure %}https://live-static.welovemicro.com/static/img/device-dark.png{% endblock %}">
...

我建议使用 姜戈宏库。

安装:

pip install django-macros

然后在 settings.py中的 INSTALLED_APPS列表中添加 'macros'应用程序。

然后,在基本模板中加载宏自定义标记,并像这样使用它们:

{# base.html #}
{% load macros %}


{% macro title %}
{% block title %} My Cool Website {% endblock %}
{% endmacro %}
    

<html>
<head>
<title> {% use_macro title %} </title>
</head>
<body>
<h1> {% use_macro title %} </h1>
</body>
</html>