Я реализую веб-приложение 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 популярен в машинном обучении и искусственном интеллекте.
Команда 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>