Jinja2 (колба): Как передать блоки в макрос?

Я реализую веб-приложение Python Flask и пытаюсь написать макрос, в который хочу передать три блока html-кода, но не смог его запустить.

Я нашел простой пример использования jinja с fastapi здесь: https://www.slingacademy.com/article/fastapi-how-to-use-macros-in-jinja-templates/?utm_content=cmp-true Я ожидал, что это сработает, но не смог его запустить. Я попробовал и получил сообщение об ошибке: «jinja2.Exceptions.TemplateAssertionError: блок 'content' определен дважды».

{% macro panel(title, class='panel') %}
<div class = "{{ class }}">
  <h2>{{ title }}</h2>
  {% block content %}{% endblock %}
</div>
{% endmacro %}

{% call(panel, title='My Panel') %}
{% block content %}
  <p>This is a panel content.</p>
{% endblock %}
{% endcall %}

Должен ли этот пример работать или в шаблоне jinja2 есть ошибка? Есть ли альтернативный/лучший способ передачи блоков html-кода в макрос? Я узнал подход «вызывающий», но думаю, что он работает только с одним блоком кода, верно?

Спасибо!

С уважением, Джером

РЕДАКТИРОВАТЬ Похоже, что пример сильно упрощен. Я хотел бы передать в макрос 2 или 3 блока кода HTML или даже Jinja. Дело в том, что я хочу упростить использование Bootstrap Accordion, который я хочу использовать несколько раз одним и тем же способом. Расширяя пример, это выглядит примерно так:

{% macro panel(title, class='panel') %}
<div class = "{{ class }}">
  <h2>{{ title }}</h2>
  {% block content1 %}{% endblock %}
   <!-- Do some other stuff in between. -->
  {% block content2 %}{% endblock %}
</div>
{% endmacro %}

{% call(panel, title='My Panel') %}
{% block content1 %}
  <p>This is a panel content.</p>
{% endblock %}
{% block content2 %}
  <p>This is the detailed panel content.</p>
{% endblock %}
{% endcall %}

РЕДАКТИРОВАТЬ 2 Основываясь на ответе и подсказках @Detlef, я каким-то образом получил пример, работающий с наследованием шаблонов, см. ниже. В любом случае, я использую концепцию вызывающего абонента, поскольку это более очевидный способ.

моймакрос.j2:

<!doctype html>
<html>
<body>
{% macro panel(title, class='panel') %}
<div class = "{{ class }}">
    <h2>{{ title }}</h2>
    {% block content1 %}{% endblock %}
    <h3> Stuff in between. </h3>
    {% block content2 %}{% endblock %}
</div>
{% endmacro %}

{% block macrocall %}{% endblock %}
</body>
</html>

основной.j2:

{% extends "mymacro.j2" %}

{% block content1 %}
<p>Panel content1.</p>
{% endblock %}
{% block content2 %}
<p>This is content2.</p>
{% endblock %}

{% block macrocall %}
{{ panel(title='My Panel') }}
{% endblock %}
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
0
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Команда caller() дает вам возможность динамически встраивать код в макрос, который был вызван с помощью call(). Если вы присвоите имя переменной в call() и передадите атрибут в caller(), это также можно использовать с определенным именем переменной во встроенном коде.
Вы можете найти документацию здесь.

{% macro panel(title, class_='panel') %}
<div class = "{{ class_ }}">
    <h2>{{ title }}</h2>
    {{ caller() }}
</div>
{% endmacro %}
{% call() panel(title='My Panel') %}
    <p>This is a panel content.</p>
{% endcall %}

Переданный код предназначен для соответствующего вызова.

Пока имена блоков остаются уникальными в шаблоне, вы также можете использовать несколько вложенных блоков в вызове макроса.

{% call() panel(title='My Panel') %}
    {% block content1 %}
    {% endblock %}
    {% block content2 %}
    {% endblock %}
{% endcall %}

Простой пример реализации макроса для загрузочного аккордеона.

{% macro bs_accordion(items) %}
    <div class = "accordion {{ kwargs.class_ }}" id = "{{ kwargs.id }}">
        {% for k,v in items.items() -%}
        <div class = "accordion-item">
            <h2 class = "accordion-header">
                <button 
                    class = "accordion-button {% if not loop.first %}collapsed{% endif %}" 
                    type = "button" 
                    data-bs-toggle = "collapse" 
                    data-bs-target = "#collapse-{{ loop.index }}" 
                    aria-expanded = "{{ loop.first | lower }}" 
                    aria-controls = "collapse-{{ loop.index }}"
                >{{ k }}</button>
            </h2>
            <div 
                id = "collapse-{{ loop.index }}" 
                class = "accordion-collapse collapse {% if loop.first %}show{% endif %}" 
                data-bs-parent = "#{{ kwargs.id }}"
            >
                <div class = "accordion-body">
                    {{ caller(v) }}
                </div>
            </div>
         </div>
        {% endfor -%}
    </div>
{% endmacro %}

<!DOCTYPE html>
<html>
<head>
    <meta charset = "utf-8">
    <meta name = "viewport" content = "width=device-width, initial-scale=1">
    <title>Example</title>
    <link 
        href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" 
        rel = "stylesheet" 
        integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" 
        crossorigin = "anonymous">
</head>
<body>
    <main class = "container my-4">
    {% set items = { 
            'My Panel 1': ['This is the first panel.', 'This is the detailed panel content.'], 
            'My Panel 2': {'Description': 'This is the second panel.', 'Details': 'This is the detailed panel content.'}, 
            'My Panel 3': 'This is the third panel.', 
        } 
    %}

    {% call(content) bs_accordion(items, id='accordionExample') %}
        {% if content is string -%}
            {{ content }}
        {% elif content is mapping -%}
            <dl>
            {% for k,v in content.items() -%}
                <dt>{{ k }}</dt>
                <dd>{{ v }}</dd>
                {% if not loop.last -%}
                <!-- Do some other stuff in between. -->
                {% endif -%}
            {% endfor -%}
            </dl>
        {% elif content is sequence -%}
            {% for item in content -%}
                <p>{{ item }}</p>
                {% if not loop.last -%}
                <!-- Do some other stuff in between. -->
                {% endif -%}
            {% endfor -%}
        {% endif -%}
    {% endcall %}
    </main>

    <script 
        src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" 
        integrity = "sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" 
        crossorigin = "anonymous"></script>
</body>
</html>

Привет! Я добавил расширенный пример в свой вопрос. Если бы я хотел передать только один «блок» кода html или jinja, я понял, что это возможно с помощью подхода вызывающего абонента. Будет ли это также применимо в тех случаях, когда я хочу передать более одного блока кода?

Jerome 21.05.2024 10:14

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

Detlef 21.05.2024 18:31

Спасибо. К сожалению, до сих пор не могу заставить его работать с "блоком". Запустив первый пример, который я привел в своем вопросе, я все равно получаю: jinja2.exceptions.TemplateAssertionError: block 'content' defined twice Вы пробовали этот пример? Вы реализовали макрос так же, как я? Спасибо

Jerome 21.05.2024 22:34

Да, я проверил пример. Сообщение об ошибке указывает на то, что блок с именем «контент» дублируется. Блок — это определение расширения шаблона. См. наследование шаблонов. Блок в родительском шаблоне заменяется одноименным блоком в дочернем шаблоне. В моем случае указанный выше макрос и его вызов реализованы в родительском шаблоне, а блоки заменяются дочерним шаблоном.

Detlef 22.05.2024 03:03

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

Detlef 22.05.2024 03:14

Благодаря вашему расширенному примеру теперь я понял. До сих пор я не до конца понял концепцию звонка и звонящего. Теперь, используя ваш пример, я вижу, что можно передать в макрос другую строку/код на основе аргумента «вызов» (в данном случае называемого «контент»). Кроме того, благодаря вашему намеку на наследование шаблонов, мне удалось каким-то образом его запустить. Для полноты я поделюсь кодом в дополнительном ответе. В любом случае, я выберу концепцию вызова, поскольку это более очевидный способ. Большое спасибо за вашу помощь.

Jerome 22.05.2024 09:57

Другие вопросы по теме