Created
September 8, 2024 11:44
-
-
Save tejas-kr/790b937cadaa2dfba3fdb039c5cd4547 to your computer and use it in GitHub Desktop.
A FastAPI CRUD app boilerplate which uses Pydantic, SQLAlchemy and Alembic
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# main.py | |
from fastapi import Depends, FastAPI, HTTPException | |
from sqlalchemy.orm import Session | |
from database import SessionLocal, engine | |
import crud, models, schemas | |
models.Base.metadata.create_all(bind=engine) | |
app = FastAPI() | |
# Dependency | |
def get_db(): | |
db = SessionLocal() | |
try: | |
yield db | |
finally: | |
db.close() | |
@app.post("/users/", response_model=schemas.User) | |
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): | |
db_user = crud.get_user_by_email(db, email=user.email) | |
if db_user: | |
raise HTTPException(status_code=400, detail="Email already registered") | |
return crud.create_user(db=db, user=user) | |
@app.get("/users/{user_id}", response_model=schemas.User) | |
def read_user(user_id: int, db: Session = Depends(get_db)): | |
db_user = crud.get_user(db, user_id=user_id) | |
if db_user is None: | |
raise HTTPException(status_code=404, detail="User not found") | |
return db_user | |
# database.py | |
from sqlalchemy import create_engine | |
from sqlalchemy.ext.declarative import declarative_base | |
from sqlalchemy.orm import sessionmaker | |
SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" | |
engine = create_engine( | |
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} | |
) | |
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | |
Base = declarative_base() | |
# models.py | |
from sqlalchemy import Column, Integer, String | |
from database import Base | |
class User(Base): | |
__tablename__ = "users" | |
id = Column(Integer, primary_key=True, index=True) | |
email = Column(String, unique=True, index=True) | |
hashed_password = Column(String) | |
# schemas.py | |
from pydantic import BaseModel | |
class UserBase(BaseModel): | |
email: str | |
class UserCreate(UserBase): | |
password: str | |
class User(UserBase): | |
id: int | |
class Config: | |
orm_mode = True | |
# crud.py | |
from sqlalchemy.orm import Session | |
import models, schemas | |
def get_user(db: Session, user_id: int): | |
return db.query(models.User).filter(models.User.id == user_id).first() | |
def get_user_by_email(db: Session, email: str): | |
return db.query(models.User).filter(models.User.email == email).first() | |
def create_user(db: Session, user: schemas.UserCreate): | |
fake_hashed_password = user.password + "notreallyhashed" | |
db_user = models.User(email=user.email, hashed_password=fake_hashed_password) | |
db.add(db_user) | |
db.commit() | |
db.refresh(db_user) | |
return db_user | |
# alembic.ini | |
# ... (typical alembic.ini content) | |
# env.py (in alembic directory) | |
from logging.config import fileConfig | |
from sqlalchemy import engine_from_config | |
from sqlalchemy import pool | |
from alembic import context | |
from database import Base | |
# this is the Alembic Config object, which provides | |
# access to the values within the .ini file in use. | |
config = context.config | |
# Interpret the config file for Python logging. | |
# This line sets up loggers basically. | |
fileConfig(config.config_file_name) | |
# add your model's MetaData object here | |
# for 'autogenerate' support | |
target_metadata = Base.metadata | |
# other values from the config, defined by the needs of env.py, | |
# can be acquired: | |
# my_important_option = config.get_main_option("my_important_option") | |
# ... etc. | |
def run_migrations_offline(): | |
"""Run migrations in 'offline' mode.""" | |
url = config.get_main_option("sqlalchemy.url") | |
context.configure( | |
url=url, | |
target_metadata=target_metadata, | |
literal_binds=True, | |
dialect_opts={"paramstyle": "named"}, | |
) | |
with context.begin_transaction(): | |
context.run_migrations() | |
def run_migrations_online(): | |
"""Run migrations in 'online' mode.""" | |
connectable = engine_from_config( | |
config.get_section(config.config_ini_section), | |
prefix="sqlalchemy.", | |
poolclass=pool.NullPool, | |
) | |
with connectable.connect() as connection: | |
context.configure( | |
connection=connection, target_metadata=target_metadata | |
) | |
with context.begin_transaction(): | |
context.run_migrations() | |
if context.is_offline_mode(): | |
run_migrations_offline() | |
else: | |
run_migrations_online() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment