article

Sunday, February 12, 2023

FastAPI Registration Form Validation with sqlalchemy Jinja2 Template

FastAPI Registration Form Validation with sqlalchemy Jinja2 Template

Install Fastapi

https://github.com/tiangolo/fastapi

pip install fastapi
C:\fastAPI\registration>pip install fastapi
C:\fastAPI\registration>pip install "uvicorn[standard]"

Install sqlalchemy jinja2
pip install python-multipart sqlalchemy jinja2
C:\fastAPI\registration>pip install python-multipart sqlalchemy jinja2

Create main.py
registration/main.py
registration/main.py
 
#registration/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 import responses
from sqlalchemy.exc import IntegrityError
from fastapi.responses import RedirectResponse
 
from forms import UserCreateForm

models.Base.metadata.create_all(bind=engine)
 
templates = Jinja2Templates(directory="templates")
 
app = FastAPI()
 
def get_db():
    db = sessionlocal()
    try:
        yield db
    finally:
        db.close()
 
@app.get("/")
async def home(request: Request, db: Session = Depends(get_db)):
    return templates.TemplateResponse("index.html", {"request": request})
 
@app.post("/register/")
async def register(request: Request, username: str = Form(...), email: str = Form(...), password: str = Form(...), db: Session = Depends(get_db)):
    form = UserCreateForm(request)
    await form.load_data()
    print("submited")
    if await form.is_valid():
        try:
            print(username)
            print(email)
            print(password)
            
            total_row = db.query(models.User).filter(models.User.email == email).first()
            print(total_row)
            if total_row == None:
                print("Save")
                users = models.User(username=username, email=email, password=password)
                db.add(users)
                db.commit()

                return responses.RedirectResponse(
                    "/", status_code=status.HTTP_302_FOUND
                ) 
            else:
                print("taken email")  
                errors = ["The email has already been taken"]  

        except IntegrityError:
            return {"msg":"Error"}
    else:
        print("Error Form")
        errors = form.errors

    return templates.TemplateResponse("index.html", {"request": request, "errors": errors})      
registration/database.py
 
#registration/database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
 
DB_URL = 'sqlite:///fastapidb.sqlite3'
 
engine = create_engine(DB_URL, connect_args={'check_same_thread': False})
sessionlocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()   
registration/models.py
 
#registration/models.py
from sqlalchemy import Column, Integer, String
from database import Base
 
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    username = Column(String(150))
    email = Column(String(150))
    password = Column(String(150))
 
    def __repr__(self):
        return '<User %r>' % (self.id)  
registration/forms.py
 
#registration/forms.py
from typing import List
from typing import Optional

from fastapi import Request

class UserCreateForm:
    def __init__(self, request: Request):
        self.request: Request = request
        self.errors: List = []
        self.username: Optional[str] = None
        self.email: Optional[str] = None
        self.password: Optional[str] = None

    async def load_data(self):
        form = await self.request.form()
        self.username = form.get("username")
        self.email = form.get("email")
        self.password = form.get("password")

    async def is_valid(self):
        if not self.username or not len(self.username) > 3:
            self.errors.append("Username should be > 3 chars")
        if not self.email or not (self.email.__contains__("@")):
            self.errors.append("Email is required")
        if not self.password or not len(self.password) >= 4:
            self.errors.append("Password must be > 4 chars")
        if not self.errors:
            return True
        return False 
make templates inside the templates directory

registration/templates/base.html
//registration/templates/index.html
{% extends 'base.html' %}
 
{% block content %}
<div class="container">
    <div class="row">
      <div class="text-danger font-weight-bold">
        <ul>
          {% for error in errors %}
            <li class="alert alert-danger">{{error}}</li>
          {% endfor %}
        </ul>  
      </div>
    </div>

    <div class="row my-5">
        <form action = "/register" method = "post"> 
        <div class="mb-3">
          <label>Username</label>
          <input type="text" required class="form-control" name="username" value="" placeholder="username">
        </div>
        <div class="mb-3">
          <label>Email</label>
          <input type="text" required placeholder="Your email" name="email" value="" class="form-control">
        </div>
        <div class="mb-3">
          <label>Password</label>
          <input type="password" required placeholder="Choose a secure password" value="" name="password" class="form-control">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
      </form>
    </div>
</div>
<style>
ul {
    list-style-type: none;
}
</style>    
{% endblock content %}
registration/temmplates/base.html
//registration/temmplates/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>FastAPI Registration Form with sqlalchemy Jinja2 Template - Cairocoders</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
    <h1 class="page-header text-center">FastAPI Registration Form Validation with sqlalchemy Jinja2 Template</h1>
    {% block content %}
         
    {% endblock content %}
</div>
</body>
</html> 
run the FastAPI app

C:\fastAPI\registration>uvicorn main:app --reload
with uvicorn using the file_name:app_instance
open the link on the browser http://127.0.0.1:8000/
http://127.0.0.1:8000/docs

Related Post