Как повторить "блок" в шаблоне django

Я хочу использовать тот же самый {% 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 в /

Тег

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

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

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

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

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

Ответ 1

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

#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

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

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

или

http://www.djangosnippets.org/snippets/363/  (Джанго & lt; 1.4)

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

Джанго & lt; 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 %}

Ответ 3

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

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

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

Ответ 4

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

Ответ 5

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

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

Требуется дополнительный файл, к сожалению, но вам не требуется передавать заголовок из представления.

Ответ 6

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

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

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

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

Ответ 7

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

Ответ 8

Ниже приведено легкое решение, аналогичное приведенному выше тегу 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 %}

пользовательский тег (получил идею здесь: fooobar.com/questions/51053/...):

@register.simple_tag(takes_context=True)
def define(context, key, value):
    context.dicts[0][key] = value
    return ''

Также не забудьте {% load %} создать свои собственные теги или добавить их в параметры шаблона builtins, t нужно загружать их в каждый шаблон. Единственным ограничением этого подхода является {% define %}, который должен вызываться из тега блока, потому что дочерние шаблоны только отображают теги блоков, которые соответствуют родительским тэгам. Не уверен, есть ли способ обойти это. Также убедитесь, что вызов define приходит, прежде чем пытаться его использовать, очевидно.

Ответ 9

Основываясь на предположении Ван Гейла, вы можете создать теги 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 %} в другом.

Ответ 10

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

Проблема:

Следующий код шаблона Django приведет к ошибке синтаксиса шаблона, но я думаю, что это действительно "хочет" иметь определенный {% block%}, повторно используемый в условном (IE, почему синтаксис синтаксического анализатора Django на BOTH концах условного, не следует ли это только проверять условие 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

Для этого есть два простых решения.

Проще всего поставить свой заголовок в переменную контекста. Вы должны установить переменную контекста в своем представлении.

Если вы используете что-то вроде общих представлений и не имеете view.py для изображений, кошек и т.д., то вы можете пойти по пути настраиваемый тег шаблона, который устанавливает переменную в контексте.

Переход по этому маршруту позволит вам сделать что-то вроде:

{% extends "base.html" %}
{% load set_page_title %}
{% page_title "My Pictures" %}
...

Затем в вашем base.html:

...
{% block title %}{{ page_title }}{% endblock %}
...
<h1>{{ page_title }}</h1>

Ответ 12

Я использую этот ответ, чтобы все было сухим.

{% extends "base.html" %}

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

Ответ 13

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