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

打印 上一主题 下一主题

主题 1898|帖子 1898|积分 5694

title: FastAPI与Tortoise-ORM开发的神奇之旅
date: 2025/05/05 00:15:48
updated: 2025/05/05 00:15:48
author: cmdragon
excerpt:
FastAPI与Tortoise-ORM联合实现全链路开发,涵盖环境设置、数据模型定义、Pydantic模式计划及API端点实现。文章模型包含软删除功能,通过is_deleted字段实现逻辑删除。API支持创建、分页查询、条件更新和软删除操作,确保数据安全与完整性。分页机制通过offset和limit实现,条件更新利用exclude_unset=True避免未传字段被覆盖。软删除通过filter().update()实现原子操作。体系具备用户认证、输入验证、防注入和细粒度权限控制等安全特性。
categories:

  • 后端开发
  • FastAPI
tags:

  • FastAPI
  • TortoiseORM
  • 全链路开发
  • 数据模型
  • API端点
  • 分页机制
  • 软删除
扫描二维码
关注大概微信搜一搜:编程智域 前端至全栈交流与成长
探索数千个预构建的 AI 应用,开启你的下一个伟大创意https://tools.cmdragon.cn/
从API到数据库:FastAPI + Tortoise-ORM全链路开发指南

1. 环境准备与包安装

起首创建Python虚拟环境并安装必要依赖:
  1. python -m venv venv
  2. source venc/bin/activate  # Linux/Mac
  3. venv\Scripts\activate     # Windows
  4. pip install fastapi uvicorn tortoise-orm pydantic python-multipart
复制代码
2. 数据模型定义

创建包含软删除功能的文章模型:
  1. # models.py
  2. from tortoise.models import Model
  3. from tortoise import fields
  4. class Article(Model):
  5.     id = fields.IntField(pk=True)
  6.     title = fields.CharField(max_length=255)
  7.     content = fields.TextField()
  8.     author_id = fields.IntField()
  9.     created_at = fields.DatetimeField(auto_now_add=True)
  10.     updated_at = fields.DatetimeField(auto_now=True)
  11.     is_deleted = fields.BooleanField(default=False)
  12.     class Meta:
  13.         table = "articles"
  14.     class PydanticMeta:
  15.         exclude = ["is_deleted"]
复制代码
3. Pydantic模式定义

定义请求响应数据布局:
  1. # schemas.py
  2. from pydantic import BaseModel
  3. from datetime import datetime
  4. class ArticleCreate(BaseModel):
  5.     title: str
  6.     content: str
  7. class ArticleUpdate(BaseModel):
  8.     title: str | None = None
  9.     content: str | None = None
  10. class ArticleResponse(BaseModel):
  11.     id: int
  12.     title: str
  13.     content: str
  14.     author_id: int
  15.     created_at: datetime
  16.     updated_at: datetime
  17.     class Config:
  18.         orm_mode = True
复制代码
4. FastAPI应用设置

设置数据库毗连和路由:
  1. # main.py
  2. from fastapi import FastAPI, Depends, HTTPException, status
  3. from fastapi.security import OAuth2PasswordBearer
  4. from tortoise.contrib.fastapi import register_tortoise
  5. app = FastAPI()
  6. # 数据库配置
  7. DATABASE_URL = "sqlite://./db.sqlite3"
  8. register_tortoise(
  9.     app,
  10.     db_url=DATABASE_URL,
  11.     modules={"models": ["models"]},
  12.     generate_schemas=True,
  13.     add_exception_handlers=True,
  14. )
  15. # 模拟用户认证
  16. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
  17. async def get_current_user(token: str = Depends(oauth2_scheme)):
  18.     return {"id": 1}  # 模拟返回用户信息
复制代码
5. 完整API端点实现

5.1 创建文章
  1. @app.post("/articles", response_model=ArticleResponse)
  2. async def create_article(
  3.         article: ArticleCreate,
  4.         user: dict = Depends(get_current_user)
  5. ):
  6.     db_article = await Article.create(
  7.         **article.dict(),
  8.         author_id=user["id"]
  9.     )
  10.     return await ArticleResponse.from_tortoise_orm(db_article)
复制代码
5.2 分页查询
  1. @app.get("/articles", response_model=list[ArticleResponse])
  2. async def list_articles(
  3.         page: int = 1,
  4.         per_page: int = 10
  5. ):
  6.     skip = (page - 1) * per_page
  7.     query = Article.all().offset(skip).limit(per_page)
  8.     return await ArticleResponse.from_queryset(query)
复制代码
5.3 条件更新
  1. @app.patch("/articles/{article_id}", response_model=ArticleResponse)
  2. async def update_article(
  3.         article_id: int,
  4.         update_data: ArticleUpdate,
  5.         user: dict = Depends(get_current_user)
  6. ):
  7.     # 只允许作者修改自己的文章
  8.     db_article = await Article.get_or_none(id=article_id, author_id=user["id"])
  9.     if not db_article:
  10.         raise HTTPException(status_code=404, detail="Article not found")
  11.     await db_article.update_from_dict(update_data.dict(exclude_unset=True))
  12.     await db_article.save()
  13.     return await ArticleResponse.from_tortoise_orm(db_article)
复制代码
5.4 软删除
  1. @app.delete("/articles/{article_id}")
  2. async def delete_article(
  3.         article_id: int,
  4.         user: dict = Depends(get_current_user)
  5. ):
  6.     updated_count = await Article.filter(
  7.         id=article_id,
  8.         author_id=user["id"]
  9.     ).update(is_deleted=True)
  10.     if not updated_count:
  11.         raise HTTPException(status_code=404, detail="Article not found")
  12.     return {"message": "Article deleted"}
复制代码
6. 关键实现剖析

6.1 分页机制原理


  • 计算skip值:skip = (page - 1) * per_page
  • 数据库查询利用offset和limit实现分页
  • 实际生产环境建议利用游标分页(Cursor Pagination)
6.2 条件更新安全


  • 通过get_or_none确保记录存在
  • 利用filter().update()实现原子操作
  • exclude_unset=True忽略未传字段
6.3 软删除实现


  • 添加is_deleted字段
  • 重写默认查询过滤器:
  1. class SoftDeleteManager(fields.Model):
  2.     def get_queryset(self):
  3.         return super().get_queryset().filter(is_deleted=False)
复制代码
7. 课后Quiz

Q1:怎样防止SQL注入攻击?
A:利用ORM的参数化查询,避免拼接SQL语句。Tortoise-ORM会自动处理查询参数,例如:
  1. await Article.filter(title="Safe' OR 1=1--")
复制代码
实际天生的SQL会是参数化查询,特殊字符会被正确转义
Q2:为什么在更新时利用exclude_unset=True?
A:该参数会忽略未提供的字段,实现部门更新。例如用户只修改标题时,不会影响content字段
Q3:怎样实现真正的物理删除?
A:直接利用delete()方法:
  1. await Article.filter(id=1).delete()
复制代码
8. 常见报错解决方案

错误1:422 Validation Error

  • 原因:请求体不符合Pydantic模型要求
  • 解决:检查请求头Content-Type是否正确(应为application/json),确认字段类型和必填项
错误2:404 Not Found

  • 原因:尝试操作不存在的记录
  • 解决:在更新/删除前先利用get_or_none检查记录是否存在
错误3:RuntimeError - Event loop closed

  • 原因:在同步代码中调用异步方法
  • 解决:确保所有数据库操作都在async函数中执行,利用await调用
启动服务下令:
  1. uvicorn main:app --reload
复制代码
访问 http://localhost:8000/docs 即可测试API接口。本文实现的体系具备以下安全特性:

  • 用户认证隔离数据访问
  • 所有输入参数自动验证
  • 数据库操作防注入
  • 细粒度的权限控制
余下文章内容请点击跳转至 个人博客页面 大概 扫码关注大概微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:FastAPI与Tortoise-ORM开发的神奇之旅 | cmdragon's Blog
往期文章归档:


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天津储鑫盛钢材现货供应商

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表