Вопрос или проблема
Вот мой небольшой скрипт на Python:
path = Path(__file__).parent
env = Environment(
loader=FileSystemLoader(path / "templates")
)
template = env.get_template("template1.rst")
rendered = template.render(sample={"a": {"b": "c"}})
А вот мой шаблон для Jinja:
.. toctree::
:maxdepth: 3
{% for k, v in sample.items() recursive %}
- {{ k }}:
{%- if v is string %}
{{ v }}
{%- else %}
{{ loop(v) }}
{%- endif -%}
{%endfor%}
Терминал выдает мне следующую ошибку:
File "/home/jaja/Bureau/coding/bac_a_sable/sphinx_test/templates/template1.rst", line 5, in top-level template code
{% for k, v in sample.items() recursive %}
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/jaja/Bureau/coding/bac_a_sable/sphinx_test/templates/template1.rst", line 21, in template
{{ loop(v) }}
^^^^^^^^^^^^^^^^^^
File "/home/jaja/Bureau/coding/bac_a_sable/sphinx_test/templates/template1.rst", line 5, in template
{% for k, v in sample.items() recursive %}
^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: not enough values to unpack (expected 2, got 1)
Так как значение v в первом цикле равно {“b”: “c”}, это должно работать, но не работает.
Не может ли Jinja рекурсивно обрабатывать словари?
Ответ или решение
Проблема, с которой вы столкнулись, касается рекурсивного обхода словаря с использованием шаблонов Jinja2. Давайте разберем ваш код и ошибки, чтобы выявить, как мы можем это исправить.
Анализ исходного кода
Ваш скрипт Python выглядит так:
path = Path(__file__).parent
env = Environment(
loader=FileSystemLoader(path / "templates")
)
template = env.get_template("template1.rst")
rendered = template.render(sample={"a": {"b": "c"}})
Вы загружаете шаблон template1.rst
и передаете в него словарь sample
со следующей структурой: {"a": {"b": "c"}}
. Теперь давайте разберемся с шаблоном Jinja:
.. toctree::
:maxdepth: 3
{% for k, v in sample.items() recursive %}
- {{ k }}:
{%- if v is string %}
{{ v }}
{%- else %}
{{ loop(v) }}
{%- endif -%}
{%endfor%}
Проблема с рекурсивной итерацией
Ошибка, которую вы получаете (ValueError: not enough values to unpack (expected 2, got 1)
), указывает на то, что Jinja не может корректно обработать ваш цикл for
с использованием ключевого слова recursive
. Распаковывая значения из sample.items()
, шаблон ожидает два значения (ключ и значение), но когда значение является вложенным словарем, возникает ошибка.
Решение проблемы
Вместо использования loop(v)
в вашем шаблоне, необходимо провести новый цикл по вложенному словарю v
. Мы можем использовать тот же подход, применив еще один уровень циклов for
, что позволит вам обрабатывать вложенные значения. Вот как может выглядеть исправленный шаблон:
.. toctree::
:maxdepth: 3
{% macro render_dict(d) %}
{% for k, v in d.items() %}
- {{ k }}:
{%- if v is string %}
{{ v }}
{%- else %}
{{ render_dict(v) }}
{%- endif %}
{% endfor %}
{% endmacro %}
{{ render_dict(sample) }}
Объяснение внесенных изменений
-
Макрос
render_dict
: Мы создали макрос, который позволяет обрабатывать словари любой глубины. Его можно вызывать рекурсивно, что и обеспечивает корректное отображение вложенных структур. -
Условие: Внутри макроса мы проверяем, является ли значение строкой, и в случае, если это так, мы просто выводим его. В противном случае мы снова вызываем макрос для обработки вложенного словаря.
-
Упрощение вывода: Конструкция
${{ render_dict(sample) }}
позволяет нам начать процесс рендеринга, передавая исходный словарьsample
в наш макрос.
Заключение
Убедитесь, что вы включаете обработку вложенных структур в шаблоне Jinja, используя макросы, что позволяет избежать ошибок, связанных с попыткой рекурсивного обхода простым циклом for
. Применяя этот подход, вы сможете эффективно обрабатывать любое количество вложенных словарей и избежать возникающих ошибок. Если у вас есть дальнейшие вопросы, не стесняйтесь обращаться.