如何在Django模板中重复一个“块”


126

我想在同一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”标签出现了多次

一种快速而肮脏的解决方案是将块标题复制到title1title2

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

但这违反了DRY原则。因为我有很多继承模板,而且因为我不想下地狱,所以这将非常困难;-)

是否有任何技巧或解决此问题的方法?如何在模板中重复相同的块,而又不重复所有代码?


1
也请参见此问题的解决方案stackoverflow.com/q/1178743/168034
phunehehe 2010年

2
这个答案特别的问题phunehehe链接。
东武

Answers:


69

我认为在这种情况下使用上下文处理器实在是太过分了。您可以轻松地做到这一点:

#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兼容。


1
我明天可能会尝试-我一直想知道如何在模板中节省一些重复,这似乎是一个好方法。谢谢。
thebiglife

1
这种方法非常好。我将base.html分为base.html和superbase.html,因此如果您想在共享模板中放置标准标题标记(例如h1),这也可以使用。页面仍然可以覆盖标题栏的内容,并且它将在两个位置更新。
SystemParadox

2
这不允许多次使用文本,对吗?
丹尼斯·戈洛马佐夫

1
Denis Golomazov:否。在这种情况下,最好使用宏插件(请参见下文)。
dqd 2015年

1
也可以反过来应用:在定义h1的块中定义内容title。或定义的一部分的块title
MikaelLindlöf'16

83

使用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 %}

2
这是太棒了!这确实可以解决我与django循环和ajax数据循环共享模板时遇到的问题。
甘油2014年

1
好的解决方案。但是,它是“ use_macro”。“ usemacro”是错误的。
拉廷

绝对应该默认内置到Django中。
zepp.lee

19

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

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

然后,您可以通过上下文设置标题。


17
可能是干的。但是,您不想在视图中设置标题。但在模板中。
Lakshman Prasad 2010年

6
标题应在模板内设置,而不是上下文提供,您需要有一种方法来定义此“ title”变量,否则,这不是一个好的解决方案。
Guillaume Esquevin

django管理模板就是这么做的(对于{{title}}),但是在删除时定义标题并不方便。
东武

13

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

# 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 %}

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


最后,我选择了{%macro%}解决方案,该解决方案不需要新文件,总体而言,我可以完全表达自己想要表达的内容。
罗曼·斯塔科夫

简单高效。与@dqd的答案不同,这些块不需要嵌套,这对于例如facebook og标签非常有用,它可能与其他head属性具有相同的内容。赞!
benzkji

2
好答案。这似乎比@dqd的答案还要DRYer,因为您不必在每个继承base的模板中重复<h1>标记。这可能是公认的答案。
阿努帕姆

优秀的!我在更复杂的场景中使用过此场景,在该场景中,我想在顶部重复表的页脚行。而且这一<tr>行相当复杂。
卡拉姆'19


5

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

为此,重复块是一个简单而干净的实现:https : //github.com/SmileyChris/django-repeatblock

模板宏是另一个,但是作者提到它未经仔细测试:http : //www.djangosnippets.org/snippets/363/

我用了repeatblock。


4
原始的django-repeatblock存储库似乎已被删除。最好的分支似乎是github.com/phretor/django-repeatblock ; 我还找到了github.com/ydm/django-sameas,它不需要'wontfix'Django补丁。
natevw

4

作为遇到此问题的任何人的更新,我采用了上面提到的摘录,并将其转变为模板标记库django-macros,该宏使宏功能更强大,并且还明确实现了重复的块模式:django-macros


4

这是类似于上面的轻量级解决方案 do_setdo_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 %}自定义标签或将其添加到模板选项内置列表中,这样就不必在每个模板中都加载它们。这种方法的唯一局限性是{% define %}必须从一个块标记中调用它,因为子模板仅呈现与父标记匹配的块标记。不知道是否有解决办法。另外,define请确保在尝试使用电话之前先打来电话。


3

根据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 %}另一个模板获取值。


我认为这是所有方法中最优雅的解决方案。感谢Kieran和Van Gale!
罗伯特·拉克鲁瓦

这很漂亮,但是似乎最好渲染Set标记中的所有节点,否则它们将被Get一次又一次地渲染。我可以想到可能是一个好主意的原因(在页面上不同块内渲染相同的存储块),但我只是想指出一点。
acjay 2012年

3

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

问题:

以下Django模板代码将导致模板语法错误,但我认为在条件(例如,为什么Django解析器同时在两端验证语法)中重新使用已定义的{%block%}是有效的“希望”有条件的,它不应该只验证真实条件吗?)

# 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 %}


1

有两个简单的解决方案。

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

如果您使用的是通用视图之类的东西,而没有用于图片,猫等的views.py,则可以采用自定义模板标记的方式,该标记在上下文中设置变量

走这条路线将使您能够执行以下操作:

{% 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.
Jonathan

0

所选答案暗示了一种简单的解决方法,可以将一个标签包装在子模板的另一个标签中,以使它们具有相同的值。我将其用于社交图像。

子模板:

{% 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 %}">
...

-3

树枝上,您可以像这样:

# 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 %}

3
这是关于Django的问题。
弗朗索瓦·恒定
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.