Create Virtual Environment
virtualenv env
and activate it
source env/bin/activate
Installation
pip install fastapi "uvicorn[standard]" jinja2 python-multipart sqlalchemy
Now make main.py, database.py, and model.py in the same directory (in todoapp directory) and also make the templates and static directory, and its look like this,
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)
database.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)
Now let’s make templates inside the templates directory
templates/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>
templates/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 %}
templates/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 %}
static/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;
}
output
To run the FastAPI app type the following command and hit enter
uvicorn main:app --reload
open link on browser (http://127.0.0.1:8000)
Source code on GitHub: https://github.com/SoniArpit/simple-todoapp-fastapi
Join developer community to share feelings, thoughts, random stuff -> https://awwsome.dev/ (made in django)
Bye <3
Top comments (0)