Jinja를 사용하려면 Jinja2 패키지를 설치하고 기존 작업 디렉터리에 templates이라는 신규 폴더를 만들어야 한다.
이 폴더에 모든 Jinja 관련 파일이 저장된다.
Jinja2 관련 패키지들을 먼저 설치하자
pip3 install jinja2 python-multipart
templates
폴더를 생성하자
mkdir templates
이제 templates 폴더 안에 home.html
,todo.html
이라는 두 개의 파일을 만든다.
cd templates
touch {home,todo}.html
home.html
: 애플리케이션 홈 페이지용todo.html
: todo 페이지용이제 FastAPI 애플리케이션이 Jinja를 사용하도록 설정해보자
from fastapi import APIRouter, Path, HTTPException, status, Request, Depends
from fastapi.templating import Jinja2Templates
from model import Todo, TodoItem, TodoItems
todo_router = APIRouter()
todo_list = []
templates = Jinja2Templates(directory="templates/")
@todo_router.post("/todo")
async def add_todo(request: Request, todo: Todo = Depends(Todo.as_form)):
todo.id = len(todo_list) + 1
todo_list.append(todo)
return templates.TemplateResponse("todo.html",
{
"request":request,
"todos":todo_list
})
@todo_router.get("/todo", response_model = TodoItems)
async def retreive_todos(request:Request):
return templates.TemplateResponse("todo.html",
{
"request":request,
"todos":todo_list
})
@todo_router.get("/todo/{todo_id}")
async def get_single_todo(request:Request, todo_id: int =Path(..., title="The ID of the todo to retrieve")) -> dict:
for todo in todo_list:
if todo.id == todo_id:
return templates.TemplateResponse("todo.html",
{
"request":request,
"todos":todo_list
})
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Todo with supplied ID doesn't exist"
)
이 코드는 Jinja가 template 폴더를 참조해서 그 안에 있는 특정 템플릿을 사용하도록 지정한다. 템플릿은 templates.TemplateResponse()
메서드를 통해 전달된다.
(todo를 추가하는 POST 메서드는 의존성을 사용해서 입력값을 전달한다.)
from pydantic import BaseModel
from typing import List, Optional
from fastapi import Form
class Item(BaseModel):
item: str
status: str
class Todo(BaseModel):
id: Optional[int] = None
item: str
@classmethod
def as_form(
cls,
item: str = Form(...)
):
return cls(item=item)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>Packt Todo Application</title>
<link crossorigin="anonymous" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" rel="stylesheet">
<link crossorigin="anonymous" href="https://use.fontawesome.com/releases/v5.0.10/css/all.css"
integrity="sha384-+d0P83n9kaQMCwj8F4RJB66tzIwOKmrdb46+porD/OvrJ+37WqIM7UoBtwHO6Nlg" rel="stylesheet">
</head>
container-fluid
Class에 block
태그를 사용해 todo_container를 추가한다.
<body>
<header>
<nav class="navar">
<div class="container-fluid">
<center>
<h1>Packt Todo Application</h1>
</center>
</div>
</nav>
</header>
<div class="container-fluid">
{% block todo_container %}{% endblock %}
</div>
</body>
</html>
{% extends "home.html" %}
{% block todo_container %}
<main class="container">
<hr>
<section class="container-fluid">
<form method="post">
<div class="col-auto">
<div class="input-group mb-3">
<input aria-describedby="button-addon2" aria-label="Add a todo" class="form-control" name="item"
placeholder="Purchase Packt's Python workshop course" type="text"
value="{{ item }}"/>
<button class="btn btn-outline-primary" data-mdb-ripple-color="dark" id="button-addon2"
type="submit">
Add Todo
</button>
</div>
</div>
</form>
</section>
{% if todo %}
<article class="card container-fluid">
<br/>
<h4>Todo ID: {{ todo.id }} </h4>
<p>
<strong>
Item: {{ todo.item }}
</strong>
</p>
</article>
{% else %}
<section class="container-fluid">
<h2 align="center">Todos</h2>
<br>
<div class="card">
<ul class="list-group list-group-flush">
{% for todo in todos %}
<li class="list-group-item">
{{ loop.index }}. <a href="/todo/{{ loop.index }}"> {{ todo.item }} </a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
</section>
</main>
{% endblock %}
todo 템플릿이 home 템플릿을 상속한다. 또한 todo_container 블록을 정의해서 부모 템플릿이 이 템플릿의 콘텐츠를 표시할 수 있게 한다.
todo 템플릿은 모든 todo를 추출하는 라우트와 단일 todo 추출하는 라우트 모두에서 사용된다. 결과적으로 라우트에 따라 다른 콘텐츠를 렌더링하게 되는 것이다.
이제 웹 브라우저에 들어가 변경 내용이 반영되었는지 확인해보자.
홈 페이지가 제대로 작동하는지 확인하기 위해 todo 아이템을 하나 추가해보자
잘 작동된다.