我想在同一个 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 at / 'block' tag with name 'title' 出现不止一次
一个快速而肮脏的解决方案是将块标题复制到title1和title2中:
# blog.html
{% extends 'base.html' %}
{% block title1 %}My Blog{% endblock %}
{% block title2 %}My Blog{% endblock %}
但这违反了 DRY 原则。这会很困难,因为我有很多继承模板,也因为我不想下地狱 ;-)
这个问题有什么技巧或解决方法吗?如何在模板中重复相同的块,而不复制所有代码?
使用 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.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 %}
等等......看起来像DRY兼容。
title
的块内定义 h1
内容。或者定义 title
的 部分 的块。
您可能实际上不想使用块,而只想使用变量:
# base.html
<html>
<head>
<title>{{ title|default:"My Cool Website" }}</title>
</head>
<body>
<h1>{{ title|default:"My Cool Website" }}</h1>
</body>
</html>
然后通过上下文设置标题。
这是我自己尝试做同样事情时发现的一种方法:
# 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 %}
不幸的是,需要一个额外的文件,但不需要您从视图中传递标题。
<tr>
行相当复杂。
您可以多次使用 {% include subtemplate.html %}
。它与块不同,但可以解决问题。
这里有一些讨论:http://code.djangoproject.com/ticket/4529 显然 django 核心团队拒绝了这张票,因为他们认为这不是一个常用的场景,但我不同意。
重复块是简单而干净的实现:https://github.com/SmileyChris/django-repeatblock
模板宏是另一种,但作者提到它没有经过仔细测试:http://www.djangosnippets.org/snippets/363/
我使用了重复块。
作为遇到此问题的任何人的更新,我将上面提到的代码片段转换为模板标记库 django-macros,使宏更强大,并且还显式地实现了重复块模式:django-macros。
这是一个类似于上述 do_set
和 do_get
模板标签答案的轻量级解决方案。 Django 允许您将整个模板上下文传递到一个标签中,该标签可以让您定义一个全局变量。
base.html:
<!DOCTYPE html>
<html lang="en">
<head>
{% block head %}
<title>{{ title }}</title>
{% endblock %}
</head>
<body>
<h1>{{ title }}</h1>
</body>
</html>
page.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 %}
您的自定义标签或将它们添加到模板选项 builtins 列表中,这样您就不必在每个模板中加载它们。这种方法的唯一限制是必须从块标记中调用 {% define %}
,因为子模板仅呈现与父标记匹配的块标记。不确定是否有解决方法。还要确保在您尝试使用它之前调用 define
。
根据 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 %}
获取它们。
我也遇到了在我的模板文件中重复 {% block %} 的同样需要。问题是我希望在 Django 条件的任何一种情况下都使用 Django {% block %},并且我希望 {% block %} 可以被可能扩展当前文件的后续文件覆盖。 (所以在这种情况下,我想要的绝对是一个块而不是一个变量,因为我在技术上没有重复使用它,它只是出现在条件的两端。
问题:
以下 Django 模板代码将导致模板语法错误,但我认为在条件中重新使用定义的 {% block %} 是有效的“想要”(IE,为什么 Django 解析器在两端都验证语法的条件,它不应该只验证真实的条件吗?)
# 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 %} 有条件地多次插入 {% block %}。这对我有用,因为 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 %}
我使用 this answer 来保持干燥。
{% extends "base.html" %}
{% with "Entry Title" as title %}
{% block title %}{{ title }}{% endblock %}
{% block h1 %}{{ title }}{% endblock %}
{% endwith %}
有两个简单的解决方案。
最简单的方法是将您的标题放入上下文变量中。您将在视图中设置上下文变量。
如果您使用的是通用视图之类的东西,并且没有用于图片、猫等的 views.py,那么您可以采用 custom template tag that sets a variable in the context 的方式。
走这条路线将使您能够执行以下操作:
{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...
然后在你的 base.html 中:
...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>
Any variable set in the context will only be available in the same block of the template in which it was assigned. This behavior is intentional; it provides a scope for variables so that they don’t conflict with context in other blocks.
选定的答案暗示了一种简单的解决方法,即在子模板中将一个标签包装在另一个标签内,以赋予它们相同的值。我将它用于这样的社交图像。
子模板:
{% 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 %}">
...
在 twig 中,您可以这样:
# 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 %}
不定期副业成功案例分享