Попытка сформировать веб-страницу на основе шаблона Jinja2. пустой файл на выходе

Есть задача формирования веб-страницы на основе списка. Для теста накидал код. На выходе получаю пустой файл. Что делаю не так?

test.py

from jinja2 import Environment, FileSystemLoader

environment = Environment(loader=FileSystemLoader("templates/"))

results_filename = "test-jinja-result.html"
results_template = environment.get_template("test-jinja.html")

test_title = "Test Jinja2 template"

files_name = [
    {"filenameindir": "1_files", "flie_number": 1},
    {"filenameindir": "2_files", "flie_number": 2},
    {"filenameindir": "8_files", "flie_number": 5},
    {"filenameindir": "2_files", "flie_number": 7},
    {"filenameindir": "3_files", "flie_number": 3},
    {"filenameindir": "4_files", "flie_number": 4},
]

context = {
    "filenameindir": files_name,
    "test-title": test_title,
}

with open(results_filename, mode="w", encoding="utf-8") as results:
    results.write(results_template.render(context))
    print(f"... wrote {results_filename}")

test-jinja.html

{# templates/test-jinja.html #}

  <body>
    <h1>{{ test_title }}</h1>
    <ul>
      {% for file in files_name %}
      <li><em> {{ files_name.filenameindir }} </em></li>
      {% endfor %}
    </ul>
  </body>

Добавлю информации. Ошибок в коде не выдает. Файл результата выглядит так:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Results</title>
  </head>

  <body>
    <h1></h1>
    <ul>
      
    </ul>
  </body>
</html>

Вот содержимое Conext через дебаг:

введите сюда описание изображения


Ответы (1 шт):

Автор решения: insolor

Сначала совет, чтобы проще было отлавливать проблемы при работе с jinja, нужно добавить параметр undefined=StrictUndefined при создании Environment:

from jinja2 import Environment, FileSystemLoader, StrictUndefined

environment = Environment(loader=FileSystemLoader("templates/"), undefined=StrictUndefined)

Тогда jinja будет падать при рендеринге, если какую-то переменную в контексте не найдет. На коде из вопроса будет так:

...
  File "templates/test-jinja.html", line 4, in top-level template code
    <h1>{{ test_title }}</h1>
jinja2.exceptions.UndefinedError: 'test_title' is undefined

Т.е. не нашло test_title - потому что в контексте у вас test-title, а не test_title.


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

Если вы записали данные в контекст по ключу filenameindir, а пытаетесь в шаблоне вытащить files_name - это не сработает, у вас в контексте нет никакого files_name. Нужно сделать, чтобы ключи в контексте и запрашиваемые имена в шаблоне совпадали.

Сами имена должны быть через подчеркивание, а не через дефис, как вы в контексте test-title сделали. Если оставить в контексте test-title, и поменять на такое же имя в шаблоне, jinja воспримет это как арифметическую операцию test - title, и будет ругаться, что нет имени test. Поэтому в именах используйте подчеркивания.

Пример правильного контекста (под ваш шаблон):

context = {
    "files_name": files_name,
    "test_title": test_title,
}

Важны именно ключи — то что слева от двоеточия, а не имена переменных, в которых значения лежали раньше. Имена переменных вообще никак не сохраняются в словаре (контексте), соответственно jinja про них не сможет узнать.

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


Вторая проблема: в цикле в шаблоне вы берете данные не из элементов списка files_name, а из самого списка files_name.

Чтобы бралось из элементов списка файлов, нужно обращаться к переменной file:

<ul>
  {% for file in files_name %}  {# file - переменная, в которую попадают данные файла #}
  <li><em> {{ file.filenameindir }} </em></li> {# и из этой же переменной file получаем filenameindir #}
  {% endfor %}
</ul>

Полный исправленный код:

from jinja2 import Environment, FileSystemLoader, StrictUndefined

environment = Environment(loader=FileSystemLoader("templates/"), undefined=StrictUndefined)

results_filename = "test-jinja-result.html"
results_template = environment.get_template("test-jinja.html")

test_title = "Test Jinja2 template"

files_name = [
    {"filenameindir": "1_files", "flie_number": 1},
    {"filenameindir": "2_files", "flie_number": 2},
    {"filenameindir": "8_files", "flie_number": 5},
    {"filenameindir": "2_files", "flie_number": 7},
    {"filenameindir": "3_files", "flie_number": 3},
    {"filenameindir": "4_files", "flie_number": 4},
]

context = {
    "files_name": files_name,
    "test_title": test_title,
}

with open(results_filename, mode="w", encoding="utf-8") as results:
    results.write(results_template.render(context))
    print(f"... wrote {results_filename}")

Шаблон:

{# templates/test-jinja.html #}

  <body>
    <h1>{{ test_title }}</h1>
    <ul>
      {% for file in files_name %}
      <li><em> {{ file.filenameindir }} </em></li>
      {% endfor %}
    </ul>
  </body>

Готовый файл:



  <body>
    <h1>Test Jinja2 template</h1>
    <ul>
      
      <li><em> 1_files </em></li>
      
      <li><em> 2_files </em></li>
      
      <li><em> 8_files </em></li>
      
      <li><em> 2_files </em></li>
      
      <li><em> 3_files </em></li>
      
      <li><em> 4_files </em></li>
      
    </ul>
  </body>

Чтобы пустые строки не добавлялись, можно добавить черточку после % в операторах начала и конца цикла:

    <ul>
      {%- for file in files_name %}
      <li><em> {{ file.filenameindir }} </em></li>
      {%- endfor %}
    </ul>

Выходной файл:



  <body>
    <h1>Test Jinja2 template</h1>
    <ul>
      <li><em> 1_files </em></li>
      <li><em> 2_files </em></li>
      <li><em> 8_files </em></li>
      <li><em> 2_files </em></li>
      <li><em> 3_files </em></li>
      <li><em> 4_files </em></li>
    </ul>
  </body>
→ Ссылка