web-dev-qa-db-ja.com

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

同じ{%block%}を同じDjangoテンプレートで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はブロックを一度だけ表示したいので、例外が発生します。

/でのTemplateSyntaxError

「title」という名前の「block」タグが複数回表示されます

迅速で汚い解決策は、ブロックtitletitle1title2

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

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

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

121
David Arcos

この場合、コンテキストプロセッサの使用はやり過ぎだと思います。これは簡単にできます:

#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互換のように見えます。

64
dqd

Django=テンプレートマクロプラグインを使用:

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

または

https://Gist.github.com/1715202 (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 %}
79

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

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

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

17
Aaron Maenpaa

{% include subtemplate.html %}を複数回使用できます。ブロックとは異なりますが、トリックは行います。

12
Javier

ここに方法があります同じことを自分でやろうとしたときに発見しました:

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

残念ながら追加のファイルが必要ですが、ビューからタイトルを渡す必要はありません。

11
Roman Starkov

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

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

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

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

5
Robert Mao

これに遭遇した人のための更新として、私は上記のスニペットを取り、それをテンプレートタグライブラリDjango-macrosに変え、マクロをより強力にし、繰り返しブロックパターンを明示的に実装します: Django-マクロ

4
Nick

上記の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呼び出しが行われていることを確認してください。

4
manncito

私も、テンプレートファイルで{%block%}を繰り返す必要があることに気付きました。問題は、Django {%block%}をDjango条件付きのいずれかの場合に使用し、{%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 %}
3
potench

Van Galeの提案に基づいて、templatetags.pyファイルに以下を追加することで、取得および設定タグを作成できます。

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 %}経由で値を取得します。

3
kieran hervold

これには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>
1
Van Gale

この回答 を使用して、物事を乾燥させます。

{% extends "base.html" %}

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