Использование шаблонов Jinja в FastAPI
Для начала нам нужно установить пакет Jinja.
pip install jinja2
Мы также добавим Bootstrap, он будет загружен из CDN. Однако дополнительные стили можно хранить в другой папке. Мы рассмотрим обслуживание статических файлов в следующих разделах курса.
Создадим новую папку templates, в каталоге нашего проекта. В этой папке будут храниться все наши файлы Jinja, которые представляют собой файлы HTML. Jinja должен определить эту папку путем создания экземпляра Jinja2Templates в FastAPI.
Вверху нашего проекта, добавим следующий код:
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
Теперь Jinja понимает, что нужно искать HTML-файлы внутри этой папки шаблонов. Далее перейдем к написанию кода шаблонов.
Создание макета шаблона
Создадим файл main.html в папке шаблонов, этот шаблон является базовым или родительским шаблоном для нашего приложения:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CRUD Project FastAPI</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
</head>
<body>
<header>
<nav class="navar">
<div class="p-3 mb-2 bg-primary text-white">
<h1>CRUD Application</h1>
</div>
</nav>
</header>
<div class="container-fluid">
{% block crud_container %}
{% endblock %}
</div>
</body>
</html>
Другие шаблоны могут наследовать структуру и дизайн этого файла main.html с помощью тегов {% extends %}. Базовый шаблон имеет теги Jinja, а именно теги {% block crud_container %} и {% endblock %}, которые указывают, куда дочерние шаблоны могут вставлять свой контент.
Создадим файл message.html и добавим в него следующий код:
{% extends "main.html" %}
{% block crud_container %}
<section class="container-fluid">
<h2>Messages</h2>
<br>
<div class="card">
<ul class="list-group list-group-flush">
{% for message in messages %}
<li class="list-group-item">{{ message.id }}. <a href="/message/{{message.id}}">{{ message.text }}</a></li>
{% endfor %}
</ul>
</div>
</section>
{% endblock %}
В данном коде мы наследуемся от базового шаблона, чтобы следовать принципу DRY(не повторяйся) и выводим в цикле наши записи, в виде маркированного списка выводим ID и сам текст записи. Теперь давайте изменим код вывода всех наших записей из БД(списка).
from fastapi import FastAPI, status, Body, HTTPException, Request
from fastapi.responses import HTMLResponse
@app.get("/")
def get_all_messages(request: Request) -> HTMLResponse:
return templates.TemplateResponse("message.html", {"request": request, "messages": messages_db})
В этой функции мы получаем фактический запрос(Request) и возвращаем templetes.TemplateResponse(HTMLResponse) с запросом в словаре. Этот словарь называется контекстным словарём. В нем мы передаем сам запрос и нашу БД(список) в виде переменной messages. Если мы добавим оператор печати print(dir(request)), мы увидим, что запрос имеет много важных атрибутов, таких как 'user', 'cookies', 'form', 'get', 'headers', 'path_params', 'query_params', 'url', 'url_for', 'values', которые могут использоваться в шаблонах.
Запустим сервер и проверим работу шаблонов. Мы видим что функция вывода всех записей приняла следующий вид:
Попробуем через нашу документацию добавить несколько записей и перейдем снова на главную страницу:
Мы видим что у нас вывелись все записи, хранящиеся в нашей БД(списке). При нажатии на любую запись, мы перейдем на страницу, которая будет выводить запись из словаря messages_db с принятым message_id из параметра пути.
В следующем шаге мы сделаем ее вывод также в виде шаблона.
Здравствуйте!
файл message.html
тег </main> не лишний?
@Николай_Баранов, лишний конечно, исправил, спасибо.