【问题标题】:How to repeat a "block" in a django template如何在 django 模板中重复“块”
【发布时间】:2010-10-05 09:27:30
【问题描述】:

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

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

一个快速而肮脏的解决方案是将块 title 复制到 title1title2

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

但这违反了DRY 原则。这会很困难,因为我有很多继承模板,也因为我不想下地狱 ;-)

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

【问题讨论】:

标签: django django-templates dry


【解决方案1】:

使用 Django 模板宏插件:

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

http://www.djangosnippets.org/snippets/363/ (django

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

【讨论】:

  • 这太棒了!这可以真正解决我在使用 django 循环和 ajax 数据循环共享模板时遇到的问题。
  • 很好的解决方案。但是,它是“use_macro”。 “usemacro”是错误的。
  • 绝对应该默认内置到Django中。
  • 这个解决方案已经很老了,kwacros 不再有效(我在 Python 3.9,Django 3.2.8 上)
【解决方案2】:

我认为在这种情况下使用上下文处理器是一种矫枉过正。您可以轻松做到这一点:

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

【讨论】:

  • 我明天可能会试试这个 - 我一直想知道如何在模板中保存一些重复,这似乎是一个好方法。谢谢。
  • 这种方法非常好。我已将我的 base.html 拆分为 base.html 和 superbase.html,因此如果您想在共享模板中放置标准标题标记(如 h1),这也适用。页面仍然可以覆盖标题栏的内容,并且会在两个位置更新。
  • 这不允许使用文本超过两次,是吗?
  • Denis Golomazov:不。在这种情况下,最好使用宏插件(见下文)。
  • 也可以反过来应用:在定义title的块内定义h1内容。或者定义title部分的块。
【解决方案3】:

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

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

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

【讨论】:

  • 可能是干的。但是您不想在视图中设置标题;但在模板中。
  • 标题应该在模板中设置,而不是由上下文提供,你需要有一种方法来定义这个“标题”变量,否则这不是一个好的解决方案。
  • 这就是 django 管理模板所做的(对于 {{title}}),但是在删除时定义标题很不方便。
【解决方案4】:

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

# 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 标签非常有用。点赞!
  • 很好的答案。这似乎比@dqd 的答案更干燥,因为您不必在每个继承基础的模板中重复

    标记。这可能是公认的答案。

  • 太棒了!我在更复杂的场景中使用了它,我想在顶部重复表格的页脚行。而&lt;tr&gt; 行相当复杂。
【解决方案5】:

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

【讨论】:

【解决方案6】:

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

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

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

我使用了重复块。

【讨论】:

【解决方案7】:

作为遇到此问题的任何人的更新,我将上面提到的 sn-p 转换为模板标记库 django-macros,使宏更强大,并且还显式地实现了重复块模式:@ 987654321@.

【讨论】:

  • 2021 年仍然有效!
  • 这个答案指的是哪个sn-p?
【解决方案8】:

这是一个类似于上面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 调用在您尝试明显使用它之前。

【讨论】:

    【解决方案9】:

    根据 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 一遍又一遍地渲染。我可以想到可能是个好主意的原因(在页面上的不同块中呈现相同的存储块),但我只是想指出这一点。
    【解决方案10】:

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

    问题:

    以下 Django 模板代码将导致模板语法错误,但我认为在条件(IE,为什么 Django 解析器验证语法)中重新使用定义的 {% block %} 是有效的“想要”在条件的两端,它不应该只验证 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 %}
    

    【讨论】:

      【解决方案11】:

      我使用this answer 保持干燥。

      {% extends "base.html" %}
      
      {% with "Entry Title" as title %}
          {% block title %}{{ title }}{% endblock %}
          {% block h1 %}{{ title }}{% endblock %}
      {% endwith %}
      

      【讨论】:

        【解决方案12】:

        有两种简单的解决方案。

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

        如果您使用的是通用视图之类的东西,并且没有用于图片、猫等的 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.
        【解决方案13】:

        选择的答案暗示了一种简单的解决方法,即在子模板中将一个标签包装在另一个标签内,以赋予它们相同的值。我将它用于这样的社交图像。

        子模板:

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

        【讨论】:

          【解决方案14】:

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

          【讨论】:

          • 这是一个关于 Django 的问题。
          猜你喜欢
          • 2021-08-03
          • 1970-01-01
          • 2014-01-22
          • 2012-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-07-11
          • 2015-06-01
          相关资源
          最近更新 更多