以下是一个使用 FastAPI 搭建的复杂 Web 应用案例,涵盖了数据库集成、表单处理、用户认证和会话管理等功能:
项目结构
fastapi-complex-app/
├── main.py
├── models.py
├── schemas.py
├── database.py
├── auth.py
└── .env
代码说明
main.py
这是 FastAPI 应用的主文件,负责定义路由、启动服务器和处理请求.
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy.orm import Session
from . import models, schemas, database, auth
from .database import SessionLocal, engine
from .auth import get_current_user
models.Base.metadata.create_all(bind=engine)
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
user = auth.authenticate_user(db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token = auth.create_access_token(data={"sub": user.username})
return {"access_token": access_token, "token_type": "bearer"}
@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = models.get_user_by_username(db, username=user.username)
if db_user:
raise HTTPException(status_code=400, detail="Username already registered")
return models.create_user(db=db, user=user)
@app.get("/users/me", response_model=schemas.User)
async def read_users_me(current_user: schemas.User = Depends(get_current_user)):
return current_user
@app.post("/items/", response_model=schemas.Item)
def create_item_for_user(
item: schemas.ItemCreate, current_user: schemas.User = Depends(get_current_user), db: Session = Depends(get_db)
):
return models.create_user_item(db=db, item=item, user_id=current_user.id)
@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = models.get_items(db, skip=skip, limit=limit)
return items
models.py
定义了用户和待办事项的模型,使用 ORM 将数据库表映射为 Python 类.
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String, unique=True, index=True)
hashed_password = Column(String)
items = relationship("Item", back_populates="owner")
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User", back_populates="items")
def get_user_by_username(db: Session, username: str):
return db.query(User).filter(User.username == username).first()
def create_user(db: Session, user: schemas.UserCreate):
hashed_password = auth.get_password_hash(user.password)
db_user = User(username=user.username, hashed_password=hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user
def get_items(db: Session, skip: int = 0, limit: int = 100):
return db.query(Item).offset(skip).limit(limit).all()
def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):
db_item = Item(**item.dict(), owner_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item
schemas.py
定义了 Pydantic 模型,用于数据验证和序列化.
from pydantic import BaseModel
from typing import Optional, List
class UserBase(BaseModel):
username: str
class UserCreate(UserBase):
password: str
class User(UserBase):
id: int
items: List["Item"] = []
class Config:
orm_mode = True
class ItemBase(BaseModel):
title: str
description: Optional[str] = None
class ItemCreate(ItemBase):
pass
class Item(ItemBase):
id: int
owner_id: int
owner: User
class Config:
orm_mode = True
database.py
配置了数据库连接和会话管理.
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from . import models
DATABASE_URL = "sqlite:///./complex_app.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
models.Base.metadata.create_all(bind=engine)
auth.py
实现了用户认证和授权逻辑,包括密码加密、JWT 令牌生成和验证等.
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from . import schemas, models, database
from .database import SessionLocal
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def get_user(db: Session, username: str):
return db.query(models.User).filter(models.User.username == username).first()
def authenticate_user(db: Session, username: str, password: str):
user = get_user(db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Depends(oauth2_scheme), db: Session = Depends(database.get_db)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = schemas.TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(db, username=token_data.username)
if user is None:
raise credentials_exception
return user
.env
用于存储环境变量,例如数据库连接字符串和 JWT 密钥等.
DATABASE_URL=sqlite:///./complex_app.db
SECRET_KEY=your_secret_key
运行项目
-
确保你已经安装了 FastAPI、SQLAlchemy、Pydantic、Passlib 和 Python-Jose 等依赖库. 如果还没有安装,可以使用以下命令安装:
pip install fastapi sqlalchemy pydantic passlib python-jose
-
将上述代码保存到相应的文件中,确保文件结构正确.
-
在终端中,导航到项目目录并运行以下命令以启动应用:
uvicorn main:app --reload
-
打开浏览器,访问
http://127.0.0.1:8000/docs
,可以查看和测试 API 文档.
这个示例展示了如何使用 FastAPI 创建一个包含数据库集成、表单处理、用户认证和会话管理的复杂 Web 应用,你可以根据需要进一步扩展功能和优化性能.