djangoテンプレートで「ブロック」を繰り返す方法


126

同じdjangoテンプレートで同じ{%block%}を 2回使用したいと思います。このブロックをベーステンプレートに複数回表示します。

# 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がブロックを1回だけ表示することを望んでいるため、例外が発生します。

/のTemplateSyntaxError

「タイトル」という名前の「ブロック」タグが複数回表示されています

素早くて汚い解決策は、ブロックのタイトルtitle1title2に複製することです

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

しかし、これはDRY原則の違反です。継承するテンプレートがたくさんあり、また地獄に行きたくないので、それは非常に難しいでしょう;-)

この問題に対するトリックや回避策はありますか?すべてのコードを複製せずに、テンプレートで同じブロックを繰り返すにはどうすればよいですか?


1
この質問へのソリューションもを参照してくださいstackoverflow.com/q/1178743/168034
phunehehe

2
特に phuneheheがリンクしている質問に対するこの回答を参照してください。
東武

回答:


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 2009

1
このアプローチは優れています。私はbase.htmlをbase.htmlとsuperbase.htmlに分割したので、共有テンプレートに標準のタイトルマークアップ(h1など)を配置する場合にも機能します。ページは引き続きタイトルブロックのコンテンツをオーバーライドでき、両方の場所で更新されます。
SystemParadox 2011

2
これはテキストを2回以上使用することを許可しませんか?
Dennis Golomazov 2015年

1
Denis Golomazov:いいえ。その場合は、マクロプラグインを使用することをお勧めします(以下を参照)。
dqd 2015年

1
逆に適用することもできます:を定義するh1ブロック内のコンテンツを定義しtitleます。または定義ブロック部分のをtitle
MikaelLindlöf2016年

83

Djangoテンプレートマクロプラグインを使用します。

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

または

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

ジャンゴ> = 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 %}

ジャンゴ<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」は間違っています。
ラムティン2017年

デフォルトで間違いなくDjangoに組み込まれるべきです。
zepp.lee 2018年

19

おそらく、実際にはブロックを使用するのではなく、変数を使用するだけです。

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

次に、コンテキストを通じてタイトルを設定します。


17
おそらくドライ。しかし、ビュー内でタイトルを設定したくないでしょう。しかし、テンプレートで。
ラクシュマンプラサード

6
タイトルは、コンテキストによって提供されるのではなく、テンプレート内から設定する必要があります。この「タイトル」変数を定義する方法が必要でした。そうでない場合、これは適切な解決策ではありません。
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%}ソリューションに落ち着き、全体として、表現したいものを正確に表現できるようになりました。
ローマスターコフ2009年

シンプルで効率的。@dqdの回答とは異なり、ブロックをネストする必要はありません。たとえば、facebookのogタグなど、他のヘッド属性と同じコンテンツを持つ場合に非常に役立ちます。賛成投票!
ベンクジ2018年

2
すばらしい答えです。これは、ベースを継承する各テンプレートで<h1>タグを繰り返す必要がないため、@ dqdの回答よりもさらに乾燥しているように見えます。これは受け入れられる答えになる可能性があります。
Anupam

優れた!これを、テーブルのフッター行を上に繰り返したいというより複雑なシナリオで使用しました。そして、<tr>列はかなり複雑でした。
キャラム


5

ここでいくつかの議論があります:http : //code.djangoproject.com/ticket/4529 明らかにdjangoコアチームはこれが一般的な使用シナリオではないと考えているため、このチケットを拒否しますが、私は同意しません。

繰り返しブロックはこれのためのシンプルでクリーンな実装です:https : //github.com/SmileyChris/django-repeatblock

テンプレートマクロは別のものですが、作者は慎重にテストされていないと述べていますhttp : //www.djangosnippets.org/snippets/363/

リピートブロックを使用しました。


4
元のdjango-repeatblockリポジトリは削除されたようです。その最高のフォークはgithub.com/phretor/django-repeatblockのようです。「wontfix」Djangoパッチを必要としないgithub.com/ydm/django-sameasも見つかりました。
natevw 14

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)

次に、1つのテンプレートで値を設定{% set foo %}put data here{% endset %}{% get foo %}、別のテンプレートでそれらを取得します。


それが最もエレガントなソリューションだと思います。キーランとヴァンゲイルに感謝します!
Robert Lacroix

これはかなり洗練されていますが、Setタグ内のすべてのノードをレンダリングする方が良いようです。そうでない場合、Getによって繰り返しレンダリングされます。私は良い考えかもしれない理由を考えることができます(ページ上の異なるブロック内に同じ格納されたブロックをレンダリングする)が、私はそれを指摘したいと思っただけです。
acjay 2012

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

2

私はこの答えを使って物事を乾いた状態に保ちます。

{% extends "base.html" %}

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

1

これには2つの簡単な解決策があります。

最も簡単なのは、タイトルをコンテキスト変数に入れることです。ビューでコンテキスト変数を設定します。

一般的なビューのようなものを使用していて、写真や猫などの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.
ジョナサン・

0

選択された答えは、子テンプレートの1つのタグを別のタグの内側にラップして、両方に同じ値を与える簡単な回避策を示しています。私はそういうソーシャルイメージにこれを使います。

子テンプレート:

{% 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に関する質問です。
フランソワ・コンスタント
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.