Як повторити "блок" у шаблоні джанго


126

Я хочу використовувати той самий {% 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 %}

Я отримаю виняток, оскільки Джанго хоче, щоб блок з’явився лише один раз:

TemplateSyntaxError у /

тег "block" з ім'ям "title" з'являється не один раз

Швидке та брудне рішення - це дублювання заголовка блоку на title1 та title2 :

# 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

1
Такий підхід відмінний. Я розділив свій base.html на base.html та superbase.html, тому це також працює, якщо ви хочете поставити стандартну розмітку заголовка (наприклад, h1) у своїх спільних шаблонах. Сторінки все ще можуть змінити вміст заголовкового блоку, і він буде оновлюватися в обох місцях.
SystemParadox

2
Це не дозволяє використовувати текст більше двох разів, чи не так?
Денніс Голомазов

1
Денис Голомазов: Ні. У цьому випадку краще скористатися макроплагіном (див. Нижче).
dqd

1
Можна також застосувати навпаки: визначення h1вмісту всередині блоку, який визначає title. Або блок , який визначає частина з title.
Мікаел Ліндльоф

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.
Гліцерин

1
Гарне рішення. Однак це "use_macro". "usemacro" неправильно.
Рамтін

Очевидно, має бути вбудований у Django за замовчуванням.
zepp.lee

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
Заголовки слід встановлювати зсередини шаблонів, а не надавати їх контекстом, потрібно мати спосіб визначити цю змінну "title", інакше це не гарне рішення.
Гійом Ескєвін

Ось що роблять шаблони адміністратора 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%}, який не потребує нового файлу, і в цілому дозволяє мені висловити саме те, що я хочу висловити.
Роман Старков

спрощений і ефективний. На відміну від відповіді @dqd, блоки не потрібно вкладати, що дуже корисно, наприклад, для тегів у Facebook та og, які можуть мати той самий вміст, що й інші атрибути голови. піднести!
benzkji

2
Чудова відповідь. Це здається навіть DRYer, ніж відповідь @ dqd, оскільки вам не доведеться повторювати тег <h1> у кожному шаблоні, який успадковує базу. Це може бути прийнята відповідь.
Анупам

Відмінно! Я використовував це в більш складних сценаріях, де хотів повторити рядок нижнього колонтитулу таблиці зверху. І <tr>ряд був досить складним.
caram

12

ви можете використовувати {% include subtemplate.html %}не один раз. це не те саме, що блоки, але робить трюк.


У цьому однакова проблема. Базовий шаблон не буде знати, який підтемп.
Van Gale

Зверніть увагу, що includeце повільніше, ніж block. docs.djangoproject.com/en/1.10/topics/performance/…
Wtower

5

Тут є деякі дискусії: http://code.djangoproject.com/ticket/4529 Очевидно, що основна команда django відкидає цей квиток, оскільки вони вважають, що це не звичайний використаний сценарій, проте я не згоден.

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

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

Я використовував повторний блок.


4
Оригінальний сховище django-repeblock видається видаленим. Найкращим видом цього здається github.com/phretor/django-repeatblock ; Я також знайшов github.com/ydm/django-sameas , який не потребує патчу Django 'wontfix'.
natevw

4

Як оновлення для всіх, хто стикається з цим, я взяв згаданий вище фрагмент і перетворив його в бібліотеку тегів шаблонів, django-macros, робить макроси більш потужними, а також явно реалізує повторний блок-шаблон: django-macros .


4

Ось легкий розчин, подібний до вищезгаданого 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дзвінок надходить, перш ніж спробувати використовувати його.


3

Спираючись на пропозицію Van Gale, ви можете створити теги 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 %}в іншому.


Я думаю, що це найелегантніше рішення з усіх. Дякую Кірану та Ван Гейлу!
Роберт Лакруа

Це досить гладко, але, здається, може бути ще краще візуалізувати всі вузли тегу Set, інакше вони отримуватимуться знову і знову Get. Я можу придумати причини, які можуть бути гарною ідеєю (надання одного і того ж збереженого блоку всередині різних блоків на сторінці), але я просто подумав, що це зазначу.
acjay

3

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

Проблема:

Наступний код шаблону Django призведе до помилки синтаксису шаблону, але я думаю, що дійсне "хочу" мати визначений {% block%} повторний використання в умовному (IE, чому аналізатор синтаксису аналізатора Django на BOTH закінчується умовного, чи не слід це лише підтверджувати ІСТИЧНУ умову?)

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


1

Для цього є два простих рішення.

Найпростіше - помістити свою назву в змінну контексту. Ви б встановили змінну контексту у вашому представленні.

Якщо ви використовуєте щось на зразок загальних представлень і не маєте view.py для зображень, котів тощо, ви можете піти шляхом користувацького тегу шаблону, який встановлює змінну в контексті .

Перехід цього маршруту дозволить вам зробити щось на кшталт:

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

Потім у вашій базі.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

Вибрана відповідь натякає на простий спосіб вирішити один тег всередині іншого в дочірньому шаблоні, щоб дати їм обом однакове значення. Я використовую це для соціальних образів так.

Дочірній шаблон:

{% 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
Це питання про Джанго.
Франсуа Констант
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.