FastAPI学习笔记2
中间件
中间件的作用:
-
预处理请求:例如检查认证头、解析 IP、记录日志、修改请求数据。
-
后处理响应:例如添加响应头(如 CORS)、压缩内容、记录响应时间。
-
全局拦截:统一处理异常、实现限流、黑白名单。
-
性能监控:统计每个请求的处理耗时。
中间件的实现:
使用 @app.middleware("http") 装饰器来实现
@app.middleware("http")
async def middleware_one(request, call_next):
print("before 1")
response = await call_next(request) # await是异步处理
print("after 1")
return response
中间件的执行顺序:
按代码自下而上执行
例如:
@app.middleware("http")
async def middleware_one(request, call_next):
print("before 1")
response = await call_next(request)
print("after 1")
return response
@app.middleware("http")
async def middleware_two(request, call_next):
print("before 2")
response = await call_next(request)
print("after 2")
return response
访问时输出顺序:
before 1
before 2
(路由处理)
after 2
after 1
依赖注入
FastAPI中,依赖注入通过抽取可复用的组件,实现代码复用、解耦且可轻松替换依赖项进行测试
怎么使用依赖注入系统?
创建依赖 -> 项导入Depends -> 声明依赖项
from fastapi import Depends
async def common_parameters(
skip: int = Query(0, ge=0),
limit: int = Query(10, le=60)
):
return {"skip": skip, "limit": limit}
@app.get("news/news_list'")
async def get_news_list(commons=Depends(common_parameters)):
return commons
ORM
SQLAlchemy ORM安装:
在命令行中执行下面代码
pip install sqlalchemy[asyncio]
建表:
第一步:创建异步引擎
mysql+aiomysql://账户:密码@localhost:3306/数据库名称?charset=utf8
ASYNC_DATABASE_URL = "mysql+aiomysql://root:root@localhost:3306/fastapi_first?charset=utf8"
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True, # 可选,输出SQL日志
pool_size=10, # 设置连接池活跃的连接数
max_overflow=20 # 允许额外的连接数
)
注意: 如果没有安装aiomysql需要先安装
pip install aiomysql
第二步:定义模型类:基类+表对应的模型类
基类:创建时间、更新时间,继承 DeclarativeBase(包含通用属性和字段的映射)
书籍表:id、书名、作者、价格、出版社
class Base(DeclarativeBase):
create_time: Mapped[datetime] = mapped_column(DateTime, insert_default=func.now(), default=func.now(),
comment="创建时间")
update_time: Mapped[datetime] = mapped_column(DateTime, insert_default=func.now(), default=func.now(),
onupdate=func.now(), comment="修改时间")
class Book(Base):
__tablename__ = "book"
id: Mapped[int] = mapped_column(primary_key=True, comment="书籍id")
bookname: Mapped[str] = mapped_column(String(255), comment="书名")
author: Mapped[str] = mapped_column(String(255), comment="者")
price: Mapped[float] = mapped_column(Float, comment="价格")
publisher: Mapped[str] = mapped_column(String(255), comment="出版社")
第三步:建表,定义函数建表 FastAPI 启动的时候调用建表的函数
async def create_tables():
# 获取异步引擎,创建事务-建表
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all) # Base 模型类的元数据创建
# 该注解表示在启动FastAPI时调用该接口
@app.on_event("startup")
async def startup_event():
await create_tables()
async with async_engine.begin() as conn:
-
async_engine是一个异步 SQLAlchemy 引擎实例,通常通过create_async_engine()创建。 -
begin()是一个异步上下文管理器,它会:-
获取一个数据库连接(从连接池中获取)。
-
开始一个事务。
-
在
async with块结束时,如果块内没有发生异常,则自动提交事务;如果发生异常,则自动回滚事务。
-
-
返回的
conn是一个异步连接对象(AsyncConnection),可以用于执行异步数据库操作。
因此,async with async_engine.begin() as conn: 确保了我们拥有一个干净的、处于事务中的数据库连接,所有在块内执行的 SQL 操作都会被包含在同一个事务中,要么全部成功提交,要么全部失败回滚。
await conn.run_sync(Base.metadata.create_all)
-
conn.run_sync()是一个特殊的方法,它允许我们在异步连接中运行一个同步函数。因为 SQLAlchemy 的核心(如metadata.create_all)原本是同步的,在异步环境中需要包装才能执行。 -
run_sync会将该同步函数放到一个线程池中执行(以避免阻塞事件循环),并等待其完成。 -
Base.metadata.create_all是 SQLAlchemy 提供的同步方法,它会:-
检查数据库中是否已存在
Base.metadata中定义的所有表。 -
如果表不存在,则根据模型定义生成
CREATE TABLE语句并执行(通过当前的连接)。 -
如果表已存在,则默认不做任何操作(不会修改表结构,也不会删除数据)。
-
-
Base是我们定义的 ORM 基类(继承自DeclarativeBase),它的metadata属性收集了所有继承自它的模型类的表定义。
因此,这行代码的作用是:在当前的数据库连接中,以同步方式执行表的创建操作,确保所有模型对应的表都被创建。
运行代码即可成功建表

路由匹配中使用ORM:
核心:创建依赖项,使用Depends 注入到处理函数
第一步:创建异步会话工厂
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
AsyncSessionLocal = async_sessionmaker(
bind=async_engine, # 绑定数据库引擎
class_=AsyncSession, # 指定会话类
expire_on_commit=False # 提交后会话不过期,不会重新查询数据库
)
第二步:创建依赖项,用于获取数据库会话
async def get_database():
async with AsyncSessionLocal() as session:
try:
yield session # 返回数据库会话给路由处理函数
await session.commit() # 提交事务
except Exception:
await session.rollback() # 有异常,回滚
raise
finally:
await session.close() # 关闭会话
第三步:Depends 注入路由处理函数
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
# 查询
result = await db.execute(select(Book))
book = result.scalars().all()
return book
ORM增删改查:
查:
查询所有数据
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
# 查询
result = await db.execute(select(Book))
book = result.scalars().all()
return book
查询第一条数据
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
# 查询
result = await db.execute(select(Book))
book = result.scalars().first()
return book
根据主键获取单条数据
@app.get("/book/books")
async def get_book_list(db: AsyncSession = Depends(get_database)):
# 查询
book = await db.get(Book,5) # 获取id为5的数据
return book
通过where进行条件查询
@app.get("/book/get_book/{book_id}")
async def get_book_list(book_id: int, db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book).where(Book.id == book_id))
book = result.scalar_one_or_none() # 若有就返回查询到的值,若没有则返回null
return book
like()模糊查询
result = await db.execute(select(Book).where(Book.author.like("曹%"))) # 查询作者以曹开头的数据
&和|查询
result = await db.execute(select(Book).where((Book.author.like("曹%"))&(Book.price > 100))) # 返回两个条件都成立的数据
result = await db.execute(select(Book).where((Book.author.like("曹%"))|(Book.price > 100))) # 返回两个条件成立其中一个的数据
in查询:将id_list中存在的id查询出来(如果数据库中不存在该id则不返回)
id_list = [1, 3, 5, 7]
result = await db.execute(select(Book).where(Book.id.in_(id_list)))
聚合计算:func.方法(模型类.属性)
-
count: 统计行数量
-
avg: 求平均值
-
max: 求最大值
-
min: 求最小值
-
sum: 求和
@app.get("/book/count")
async def get_count(db: AsyncSession = Depends(get_database)):
# result = await db.execute(select(func.count(Book.id)))
# result = await db.execute(select(func.max(Book.price)))
# result = await db.execute(select(func.sum(Book.price)))
# result = await db.execute(select(func.avg(Book.price)))
count = result.scalar()
return count
分页查询:
select().offset().limit()
-
offset: 跳过的记录数
-
limit: 返回的记录数
offset值 = (当前页码 - 1)* 每页数量limit
@app.get("/book/get_book_list")
async def get_book_list(
page: int = 1,
page_size: int = 3,
db: AsyncSession = Depends(get_database)
):
# (页码 - 1) * 每页数量
skip = (page - 1) * page_size
# offset 跳过的记录数 ; limit 每页的记录数
stmt = select(Book).offset(skip).limit(page_size)
result = await db.execute(stmt)
books = result.scalars().all()
return books
既然前端就能执行分页功能了那为什么后端还需要执行分页?
答:
1. 数据量问题
-
前端分页的局限:如果后端一次性返回所有数据,当数据量很大(例如成千上万条)时,前端需要占用大量内存来存储数据,渲染大量DOM元素会导致页面卡顿,甚至崩溃。
-
后端分页的优势:后端只返回当前页所需的数据(例如
LIMIT 10 OFFSET 20),前端只需处理少量数据,内存和渲染压力极小,保证了流畅的用户体验。
2. 网络传输效率
-
一次性传输全部数据会消耗大量带宽,延长页面加载时间,尤其在移动网络环境下体验更差。
-
后端分页只传输当前页的数据,响应快,节省流量,页面能迅速呈现内容。
3. 数据库性能
-
后端可以利用数据库的索引和分页查询机制(如
LIMIT/OFFSET、游标分页等)高效地获取数据,避免将大量数据从数据库传到应用层再过滤。 -
如果前端分页,后端仍然需要查询全部数据,这不仅浪费数据库资源,还可能拖垮数据库。
4. 安全与权限
- 某些数据可能包含敏感信息(如用户隐私、商业机密),不应全部暴露给前端。后端分页可以在服务端控制数据访问权限,只返回用户有权查看的部分。
5. 用户体验与交互模式
-
常见的分页、无限滚动、加载更多等交互,都需要后端配合按需返回数据,而不是一次性加载全部。
-
例如搜索引擎、电商商品列表,用户通常只浏览前几页,后端分页可以按需加载,提升感知性能。
6. 接口通用性
- 后端提供分页接口后,可以被多个前端(Web、移动端、第三方)复用,每个客户端按需请求数据,无需各自实现完整数据加载逻辑。
增:
核心步骤:定义ORM对象 -> 添加对象到事务:add(对象) -> commit提交到数据库
class BookBase(BaseModel):
id: int
bookname:str
author: str
price:float
publisher: str
@app.post("/book/add_book")
async def add_book(book: BookBase, db: AsyncSession = Depends(get_database)):
# ORM对象 -> add -> commit
book_obj = Book(**book.__dict__) # 将用户提交的字典对象转换成ORM对象
db.add(book_obj)
await db.commit()
return book
改:
核心步骤:查询get -> 属性重新赋值 -> commit提交到数据库
# 需求: 修改图书信息:先查再改
# 设计思路: 路径参数书籍id: 作用是查找; 请求体参数: 作用是新数据(书名、作者、价格、出版社)
class BookUpdate(BaseModel):
bookname: str
author: str
price: float
publisher: str
@app.put("/book/update_book/{book_id}")
async def update_book(book_id: int, data: BookUpdate, db: AsyncSession = Depends(get_database)):
db_book = await db.get(Book, book_id)
if db_book is None:
raise HTTPException(
status_code=404,
detail="查无此书"
)
# 找到后则修改:重新赋值
db_book.bokname = data.bookname
db_book.author = data.author
db_book.price = data.price
db_book.publisher = data.publisher
# 提交到数据库
await db.commit()
return db_book
删:
核心步骤:查询get -> delete删除 -> commit提交到数据库
@app.delete("/book/delete_book/{book_id}")
async def delete_book(book_id: int, db: AsyncSession = Depends(get_database)):
# 先查再删 提交
db_book = await db.get(Book, book_id)
if db_book is None:
raise HTTPException(
status_code=404,
detai="查无此书"
)
await db.delete(db_book)
await db.commit()
return {"msg": "删除图书成功"}
更多推荐

所有评论(0)