Как повторить «блок» в шаблоне Django - PullRequest
121 голосов
/ 04 февраля 2009

Я хочу использовать один и тот же {% block%} дважды в одном и том же шаблоне django. Я хочу, чтобы этот блок появлялся в моем базовом шаблоне более одного раза:

# 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 /

появляется тег 'block' с именем 'title' более одного раза

Быстрое и грязное решение будет дублировать блок title в title1 и title2 :

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

Но это нарушение принципа DRY . Это было бы очень сложно, так как у меня много наследующих шаблонов, а также потому, что я не хочу идти в ад; -)

Есть ли уловка или обходной путь для этой проблемы? Как я могу повторить тот же блок в моем шаблоне, не дублируя весь код?

Ответы [ 13 ]

79 голосов
/ 22 сентября 2009

Используйте плагин макроса шаблонов Django:

http://www.djangosnippets.org/snippets/363/ (Джанго <1,4) </p>

или

https://gist.github.com/1715202 (джанго> = 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 %}
63 голосов
/ 27 апреля 2009

Я думаю, что использование контекстного процессора в этом случае является излишним. Вы можете легко сделать это:

#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-совместимый.

17 голосов
/ 04 февраля 2009

Вы, вероятно, на самом деле не хотите использовать блок, а просто использовать переменную:

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

Затем вы устанавливаете заголовок в контексте.

11 голосов
/ 27 сентября 2009

Вот способ, который я обнаружил, пытаясь сделать то же самое сам:

# 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 голосов
/ 04 февраля 2009

вы можете использовать {% include subtemplate.html %} более одного раза. это не то же самое, что блоки, но делает трюк.

5 голосов
/ 23 января 2011

Здесь есть некоторые обсуждения: http://code.djangoproject.com/ticket/4529 Очевидно, основная команда django отвергает этот билет, потому что они думают, что это не распространенный сценарий, однако я не согласен.

блок повторений - простая и понятная реализация для этого: https://github.com/SmileyChris/django-repeatblock

Шаблон макросов

является еще одним, однако автор упомянул, что он не был тщательно протестирован: http://www.djangosnippets.org/snippets/363/

Я использовал repeatblock.

4 голосов
/ 11 января 2017

Вот упрощенное решение, похожее на приведенный выше шаблонный ответ 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 %} свои собственные теги или добавить их в список параметров шаблона встроенные , чтобы вам не приходилось загружать их в каждый шаблон. Единственным ограничением этого подхода является то, что {% define %} должен вызываться из тега блока, потому что дочерние шаблоны отображают только теги блока, которые соответствуют родительским тегам. Не уверен, есть ли способ обойти это. Также убедитесь, что вызов define поступил до того, как вы попытаетесь использовать его явно.

4 голосов
/ 03 октября 2014

В качестве обновления для всех, кто сталкивался с этим, я взял упомянутый выше фрагмент и превратил его в библиотеку тегов шаблонов, django-macros, делает макросы более мощными и также явно реализует шаблон повторяющихся блоков: Джанго-макросы .

3 голосов
/ 23 марта 2011

Я тоже столкнулся с такой же необходимостью повторения {% block%} в моих файлах шаблонов. Проблема в том, что я хочу, чтобы Django {% block%} использовался в любом случае условного выражения Django, и я хочу, чтобы {% block%} был перезаписан последующими файлами, которые могут расширить текущий файл. (Таким образом, в данном случае то, что я хочу, - это определенно больше блока, чем переменной, потому что я не использую его технически, он просто появляется на любом конце условного выражения.

Проблема:

Следующий код шаблона Django приведет к ошибке синтаксиса шаблона, но я думаю, что допустимо «хотеть» иметь определенный {% block%}, повторно используемый в условном выражении (IE, почему синтаксический анализатор синтаксического анализа Django проверяет синтаксис на обоих концах условия, разве это не должно проверять только условие ИСТИНА?)

# 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 включает в себя только ИСТИНА {% 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 голосов
/ 08 января 2010

Основываясь на предложении Ван Гейла, вы можете создать теги get и set, добавив в файл 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)

Затем задайте значения в одном шаблоне с помощью {% set foo %}put data here{% endset %} и получите их с помощью {% get foo %} в другом.

...