Jinja2 템플릿을 사용하는 FastAPI의 ToDo 앱

가상 환경 만들기




virtualenv env


활성화하고

source env/bin/activate


설치




pip install fastapi "uvicorn[standard]" jinja2 python-multipart sqlalchemy


이제 같은 디렉토리(todoapp 디렉토리)에 main.py, database.py 및 model.py를 만들고 템플릿과 정적 디렉토리도 만듭니다. 모양은 다음과 같습니다.



main.py




# main.py

from fastapi import FastAPI, Request, Depends, Form, status
from fastapi.templating import Jinja2Templates
import models
from database import engine, sessionlocal
from sqlalchemy.orm import Session
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles

models.Base.metadata.create_all(bind=engine)

templates = Jinja2Templates(directory="templates")

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

def get_db():
    db = sessionlocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/")
async def home(request: Request, db: Session = Depends(get_db)):
    todos = db.query(models.Todo).order_by(models.Todo.id.desc())
    return templates.TemplateResponse("index.html", {"request": request, "todos": todos})

@app.post("/add")
async def add(request: Request, task: str = Form(...), db: Session = Depends(get_db)):
    todo = models.Todo(task=task)
    db.add(todo)
    db.commit()
    return RedirectResponse(url=app.url_path_for("home"), status_code=status.HTTP_303_SEE_OTHER)

@app.get("/edit/{todo_id}")
async def add(request: Request, todo_id: int, db: Session = Depends(get_db)):
    todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first()
    return templates.TemplateResponse("edit.html", {"request": request, "todo": todo})

@app.post("/edit/{todo_id}")
async def add(request: Request, todo_id: int, task: str = Form(...), completed: bool = Form(False), db: Session = Depends(get_db)):
    todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first()
    todo.task = task
    todo.completed = completed
    db.commit()
    return RedirectResponse(url=app.url_path_for("home"), status_code=status.HTTP_303_SEE_OTHER)

@app.get("/delete/{todo_id}")
async def add(request: Request, todo_id: int, db: Session = Depends(get_db)):
    todo = db.query(models.Todo).filter(models.Todo.id == todo_id).first()
    db.delete(todo)
    db.commit()
    return RedirectResponse(url=app.url_path_for("home"), status_code=status.HTTP_303_SEE_OTHER)


데이터베이스.py




# database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

DB_URL = 'sqlite:///todo.sqlite3'

engine = create_engine(DB_URL, connect_args={'check_same_thread': False})
sessionlocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()


models.py




# models.py

from sqlalchemy import Column, Integer, Boolean, Text
from database import Base

class Todo(Base):
    __tablename__ = 'todos'
    id = Column(Integer, primary_key=True)
    task = Column(Text)
    completed = Column(Boolean, default=False)

    def __repr__(self):
        return '<Todo %r>' % (self.id)


이제 템플릿 디렉토리 안에 템플릿을 만들어 봅시다

템플릿/base.html




<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ToDo App</title>
    <link rel="stylesheet" href="{{ url_for('static', path='css/main.css') }}">
</head>
<body>
    <main>
        <h1>ToDo App</h1>
        <br>
        {% block content %}

        {% endblock content %}
    </main>
</body>
</html>


템플릿/index.html




{% extends 'base.html' %}

{% block content %}

    <form action="/add" method="post">
        <textarea name="task" rows="5" placeholder="Enter your task"></textarea>
        <button type="submit">Add</button>
    </form>

    <br>
    <h2>Tasks</h2>

    <div>
        {% for todo in todos %}
            <div class="task">
                {% if todo.completed %}
                    <strike>{{ todo.task }}</strike>
                {% else %}
                    {{ todo.task }}
                {% endif %}
                <small>
                    <a href="edit/{{ todo.id }}">Edit</a>
                    <a href="delete/{{ todo.id }}">Delete</a>
                </small>
            </div>
        {% endfor %}
    </div>

{% endblock content %}


템플릿/edit.html




{% extends 'base.html' %}

{% block content %}
    <form action="/edit/{{todo.id}}" method="post">
        <textarea name="task" rows="5">{{todo.task}}</textarea>
        <label for="completed">Done?</label>
        <input type="checkbox" name="completed" {% if todo.completed %}checked{% endif %}>

        <button type="submit">Save</button>
    </form>
{% endblock content %}


정적/css/main.css




*{
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}

body{
    font-family: 'Roboto', sans-serif;
    background: #f5f5f5;
}

main{
    width: 100%;
    max-width: 520px;
    margin: 0 auto;
    padding: 0 20px;
}

textarea{
    width: 100%;
    height: 100px;
    border: 1px solid #ccc;
    border-radius: 5px;
    padding: 10px;
    resize: none;
}

button{
    width: 100%;
    height: 40px;
    border: 1px solid #ccc;
    border-radius: 5px;
    background-color: #eee;
    color: #333;
    font-size: 16px;
    cursor: pointer;
}

.task{
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 8px 0;
    border-bottom: 1px solid #ccc;
}

.task:first-child{
    border-top: 1px solid #ccc;
}


산출



FastAPI 앱을 실행하려면 다음 명령을 입력하고 Enter 키를 누릅니다.

uvicorn main:app --reload


브라우저에서 링크 열기( http://127.0.0.1:8000 )

색인 페이지


페이지 편집


GitHub의 소스 코드: https://github.com/SoniArpit/simple-todoapp-fastapi

개발자 커뮤니티에 가입하여 감정, 생각, 무작위 항목 공유 -> https://awwsome.dev/ (django에서 제작)

안녕 <3

좋은 웹페이지 즐겨찾기