mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
715 字
2 分钟
SQLAlchemy 异步配置与 CRUD 操作
2026-06-08

SQLAlchemy 异步配置与 CRUD 操作#

技术栈:SQLAlchemy 2.0(异步) + aiomysql + MySQL


一、什么是 ORM?#

ORM(Object Relational Mapping,对象关系映射) 将数据库表映射为 Python 对象,用代码操作数据库而不用手写 SQL。

数据库概念Python 对应
表(Table)类(Class)
行(Row)实例(Object)
列(Column)属性(Attribute)
SQL 语句方法调用

二、安装依赖#

# SQLAlchemy 异步支持
uv add sqlalchemy[asyncio]
# MySQL 异步驱动
uv add aiomysql

三、完整配置(7 步)#

3.1 创建异步引擎#

from sqlalchemy.ext.asyncio import create_async_engine
ASYNC_DATABASE_URL = "mysql+aiomysql://root:password@localhost:3306/mydb?charset=utf8mb4"
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True, # 打印 SQL 语句(调试用,生产环境关闭)
pool_size=10, # 连接池大小
max_overflow=20, # 超出连接池后最多可创建的连接数
)

数据库 URL 格式:

mysql+aiomysql://用户名:密码@主机:端口/数据库名?charset=utf8mb4
部分说明示例
mysql+aiomysql数据库类型 + 异步驱动
用户名数据库用户名root
密码数据库密码password
主机数据库地址localhost
端口数据库端口3306
数据库名目标数据库mydb

常用异步驱动:

数据库安装命令URL 前缀
MySQLuv add aiomysqlmysql+aiomysql
PostgreSQLuv add asyncpgpostgresql+asyncpg
SQLiteuv add aiosqlitesqlite+aiosqlite

3.2 定义基类和模型#

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import DateTime, String, Float, func
from datetime import datetime
# 基类:所有模型都继承它
class Base(DeclarativeBase):
create_time: Mapped[datetime] = mapped_column(
DateTime,
default=func.now, # Python 层面:插入时调用
server_default=func.now(), # 数据库层面:DDL 默认值
comment="创建时间"
)
update_time: Mapped[datetime] = mapped_column(
DateTime,
default=func.now,
onupdate=func.now(), # 更新记录时自动设置
server_default=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="出版社")

Mapped / mapped_column 速查:

参数说明示例
primary_key主键mapped_column(primary_key=True)
String(n)字符串,最大长度 nmapped_column(String(255))
Float浮点数mapped_column(Float)
Integer整数mapped_column(Integer)
Boolean布尔值mapped_column(Boolean)
DateTime日期时间mapped_column(DateTime)
comment字段注释mapped_column(comment="书名")
defaultPython 层默认值default=func.now
server_default数据库层默认值server_default=func.now()
onupdate更新时自动触发onupdate=func.now()

3.3 建表函数#

async def create_table():
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

run_sync 将同步的建表操作转为异步执行。

3.4 应用生命周期管理#

from contextlib import asynccontextmanager
from fastapi import FastAPI
@asynccontextmanager
async def lifespan(app):
print("应用启动...")
await create_table() # 启动时自动建表
yield # 应用运行中
print("应用关闭...")
await async_engine.dispose() # 关闭时释放连接池
app = FastAPI(lifespan=lifespan)

3.5 创建会话工厂#

from sqlalchemy.ext.asyncio import async_sessionmaker, AsyncSession
AsyncSessionLocal = async_sessionmaker(
bind=async_engine, # 绑定引擎
class_=AsyncSession, # 使用异步会话
expire_on_commit=False # 提交后对象不过期(推荐)
)
参数说明
bind绑定的数据库引擎
class_会话类,异步场景用 AsyncSession
expire_on_commit设为 False 可避免提交后重新查询,推荐

3.6 数据库依赖项#

async def get_database():
async with AsyncSessionLocal() as session:
try:
yield session # 提供给路由
await session.commit() # 无异常则提交
except Exception:
await session.rollback() # 有异常则回滚
finally:
await session.close() # 关闭会话

3.7 路由中使用#

from fastapi import Depends
from sqlalchemy import select
@app.get("/books")
async def get_books(db: AsyncSession = Depends(get_database)):
result = await db.execute(select(Book))
return result.scalars().all()

四、CRUD 操作#

4.1 查询(Read)#

查询所有#

result = await db.execute(select(Book))
books = result.scalars().all()

查询单条#

# 通过主键查询
book = await db.get(Book, 1)
# 获取第一条
result = await db.execute(select(Book))
book = result.scalars().first()
# 恰好一条(多条抛异常,无数据返回 None)
book = result.scalar_one_or_none()
方法说明无数据时
scalars().all()所有结果[]
scalars().first()第一条None
scalar_one_or_none()一条或无None
scalar_one()恰好一条抛异常

条件查询#

from sqlalchemy import select
# 价格 >= 200
result = await db.execute(select(Book).where(Book.price >= 200))
# 精确查询
result = await db.execute(select(Book).where(Book.id == 1))
# 多条件 AND
result = await db.execute(
select(Book).where(Book.price >= 100, Book.author == "鲁迅")
)

条件运算符:

运算符含义示例
==等于Book.id == 1
!=不等于Book.id != 1
> / >=大于 / 大于等于Book.price >= 200
< / <=小于 / 小于等于Book.price <= 500
&AND(条件1) & (条件2)
|OR(条件1) | (条件2)
~NOT~条件
.in_()包含Book.id.in_([1, 2, 3])

模糊查询#

# % 匹配零个或多个字符
result = await db.execute(select(Book).where(Book.author.like("鲁%")))
# _ 匹配恰好一个字符
result = await db.execute(select(Book).where(Book.author.like("鲁_")))

聚合查询#

from sqlalchemy import func
# 平均值
avg = await db.execute(select(func.avg(Book.price)))
print(avg.scalar())
# 计数
count = await db.execute(select(func.count(Book.id)))
print(count.scalar())
# 最大值
max_price = await db.execute(select(func.max(Book.price)))
print(max_price.scalar())

分页查询#

# 分页公式:offset = (page - 1) * page_size
async def get_books(page: int = 1, page_size: int = 10, db: AsyncSession = Depends(get_database)):
skip = (page - 1) * page_size
stmt = select(Book).offset(skip).limit(page_size)
result = await db.execute(stmt)
return result.scalars().all()
方法说明
offset(n)跳过前 n 条
limit(n)最多返回 n 条

4.2 新增(Create)#

from pydantic import BaseModel
class BookCreate(BaseModel):
bookname: str
author: str
price: float
publisher: str
@app.post("/books")
async def create_book(data: BookCreate, db: AsyncSession = Depends(get_database)):
book = Book(**data.model_dump()) # Pydantic 模型转 ORM 对象
db.add(book)
await db.commit()
return book

4.3 更新(Update)#

@app.put("/books/{id}")
async def update_book(id: int, data: BookCreate, db: AsyncSession = Depends(get_database)):
book = await db.get(Book, id) # ① 查询
if book is None:
raise HTTPException(status_code=404, detail="书籍不存在")
book.bookname = data.bookname # ② 修改属性
book.author = data.author
book.price = data.price
book.publisher = data.publisher
await db.commit() # ③ 提交
return book

流程: 查询 → 修改属性 → commit,不需要 db.add()


4.4 删除(Delete)#

@app.delete("/books/{id}")
async def delete_book(id: int, db: AsyncSession = Depends(get_database)):
book = await db.get(Book, id) # ① 查询
if book is None:
raise HTTPException(status_code=404, detail="书籍不存在")
await db.delete(book) # ② 标记删除
await db.commit() # ③ 提交
return {"msg": "删除成功"}

五、CRUD 总结#

操作HTTP 方法核心步骤
GETselect()execute()scalars().all()
POST创建对象 → db.add()db.commit()
PUTdb.get() 查询 → 修改属性 → db.commit()
DELETEdb.get() 查询 → db.delete()db.commit()

六、常见问题#

Q1:expire_on_commit 有什么影响?#

设为 True(默认)时,commit 后访问对象属性会重新查询数据库。推荐设为 False,避免不必要的查询。

Q2:会话忘记关闭怎么办?#

使用 get_database() 依赖注入,try/except/finally 结构确保会话一定被关闭,不会连接泄漏。

Q3:db.add()db.commit() 的区别?#

  • db.add() — 把对象加入会话(内存中),还没写入数据库
  • db.commit() — 提交事务,真正写入数据库

Q4:更新时需要 db.add() 吗?#

不需要。查询出来的对象已经在会话中跟踪了,直接修改属性然后 commit 即可。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

SQLAlchemy 异步配置与 CRUD 操作
https://blog-miku.pages.dev/posts/sqlalchemy/
作者
eat-li
发布于
2026-06-08
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录