FastAPI与Tortoise-ORM开发的神奇之旅
title: FastAPI与Tortoise-ORM开发的神奇之旅


python -m venv venv
source venc/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
pip install fastapi uvicorn tortoise-orm pydantic python-multipart
# models.py
from tortoise.models import Model
from tortoise import fields
class Article(Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=255)
content = fields.TextField()
author_id = fields.IntField()
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
is_deleted = fields.BooleanField(default=False)
class Meta:
table = "articles"
class PydanticMeta:
exclude = ["is_deleted"]
# schemas.py
from pydantic import BaseModel
from datetime import datetime
class ArticleCreate(BaseModel):
title: str
content: str
class ArticleUpdate(BaseModel):
title: str | None = None
content: str | None = None
class ArticleResponse(BaseModel):
id: int
title: str
content: str
author_id: int
created_at: datetime
updated_at: datetime
class Config:
orm_mode = True
# main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from tortoise.contrib.fastapi import register_tortoise
app = FastAPI()
# 数据库配置
DATABASE_URL = "sqlite://./db.sqlite3"
register_tortoise(
app,
db_url=DATABASE_URL,
modules={"models": ["models"]},
generate_schemas=True,
add_exception_handlers=True,
)
# 模拟用户认证
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
return {"id": 1} # 模拟返回用户信息
@app.post("/articles", response_model=ArticleResponse)
async def create_article(
article: ArticleCreate,
user: dict = Depends(get_current_user)
):
db_article = await Article.create(
**article.dict(),
author_id=user["id"]
)
return await ArticleResponse.from_tortoise_orm(db_article)
@app.get("/articles", response_model=list[ArticleResponse])
async def list_articles(
page: int = 1,
per_page: int = 10
):
skip = (page - 1) * per_page
query = Article.all().offset(skip).limit(per_page)
return await ArticleResponse.from_queryset(query)
@app.patch("/articles/{article_id}", response_model=ArticleResponse)
async def update_article(
article_id: int,
update_data: ArticleUpdate,
user: dict = Depends(get_current_user)
):
# 只允许作者修改自己的文章
db_article = await Article.get_or_none(id=article_id, author_id=user["id"])
if not db_article:
raise HTTPException(status_code=404, detail="Article not found")
await db_article.update_from_dict(update_data.dict(exclude_unset=True))
await db_article.save()
return await ArticleResponse.from_tortoise_orm(db_article)
@app.delete("/articles/{article_id}")
async def delete_article(
article_id: int,
user: dict = Depends(get_current_user)
):
updated_count = await Article.filter(
id=article_id,
author_id=user["id"]
).update(is_deleted=True)
if not updated_count:
raise HTTPException(status_code=404, detail="Article not found")
return {"message": "Article deleted"}
class SoftDeleteManager(fields.Model):
def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)
await Article.filter(title="Safe' OR 1=1--")
await Article.filter(id=1).delete()
uvicorn main:app --reload
评论
发表评论