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

时间:2009-02-04 12:04:29

标签: django django-templates dry

我想在同一个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      出现

'block'标记,其名称为'title'   不止一次

快速而肮脏的解决方案是将块标题复制到 title1 title2

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

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

这个问题有什么技巧或解决方法吗?如何在模板中重复相同的块,而不重复所有代码?

14 个答案:

答案 0 :(得分:79)

使用Django模板宏插件:

http://www.djangosnippets.org/snippets/363/(django&lt; 1.4)

https://gist.github.com/1715202(django&gt; = 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 %}

答案 1 :(得分:63)

我认为在这种情况下使用上下文处理器是一种过度杀伤力。您可以轻松地执行此操作:

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

答案 2 :(得分:16)

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

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

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

答案 3 :(得分:11)

您可以多次使用{% include subtemplate.html %}。它与块不同,但可以解决问题。

答案 4 :(得分:11)

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

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

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

答案 5 :(得分:4)

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

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

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

我用了repeatblock。

答案 6 :(得分:4)

作为对此遇到的任何人的更新,我已经将上面提到的片段转换为模板标记库django-macros,使宏更强大,并且还明确地实现了重复的块模式:{{3 }}

答案 7 :(得分: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 %}您的自定义标记,或将其添加到模板选项builtins列表中,这样您就不必在每个模板中加载它们。此方法的唯一限制是必须在块标记内调用{% define %},因为子模板仅呈现与父标记匹配的块标记。不确定是否有办法解决这个问题。在您尝试使用它之前,请确保define来电。

答案 8 :(得分: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 %}获取。

答案 9 :(得分:3)

我也遇到过模板文件中重复{%block%}的相同需求。问题是我想在Django条件的任何一种情况下使用Django {%block%},并且我希望{%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%}多次有条件地插入{%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 %}

答案 10 :(得分:1)

有两个简单的解决方案。

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

如果您使用的是通用视图,并且没有图片,猫等的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>

答案 11 :(得分:1)

我使用this answer来保持干燥。

{% extends "base.html" %}

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

答案 12 :(得分: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 %}">
...

答案 13 :(得分:-3)

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