Python SQLAlchemy ORM 实战
直接写 SQL 容易出错且难维护,SQLAlchemy 把数据库表映射成 Python 类,用面向对象的方式操作数据库。本文从连接、模型到查询和关联,讲清 SQLAlchemy ORM 的核心用法。
一、为什么用 ORM
直接写 SQL 有几个问题:字符串拼接容易引入 SQL 注入风险、表结构改了要到处改 SQL、调试不方便。ORM(对象关系映射)把数据库表映射成 Python 类,用操作对象的方式操作数据库。
# 不用 ORM:手写 SQL
import sqlite3
conn = sqlite3.connect("test.db")
cursor = conn.execute("SELECT * FROM users WHERE age > ?", (18,))
users = cursor.fetchall()
# 用 ORM:操作对象
from models import User
adults = User.query.filter(User.age > 18).all()
ORM 选型:Python 生态最主流的是 SQLAlchemy(成熟稳定)和 Peewee(轻量简单)。新项目推荐 SQLAlchemy 2.0,支持类型提示和现代写法。
二、安装与连接
pip install sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, DeclarativeBase
# 创建引擎(连接池)
engine = create_engine(
"sqlite:///test.db", # 数据库 URL
echo=True, # 打印 SQL(开发时用)
pool_size=5, # 连接池大小
)
# 基类
class Base(DeclarativeBase):
pass
# Session 工厂
SessionLocal = sessionmaker(bind=engine)
三、定义模型
from sqlalchemy import String, Integer, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
class User(Base):
__tablename__ = "users"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
email: Mapped[str] = mapped_column(String(100), unique=True)
age: Mapped[int] = mapped_column(Integer, default=0)
# 关联:一个用户有多篇文章
posts: Mapped[list["Post"]] = relationship(back_populates="author")
class Post(Base):
__tablename__ = "posts"
id: Mapped[int] = mapped_column(primary_key=True)
title: Mapped[str] = mapped_column(String(200))
content: Mapped[str] = mapped_column(String)
author_id: Mapped[int] = mapped_column(ForeignKey("users.id"))
author: Mapped[User] = relationship(back_populates="posts")
# 创建所有表
Base.metadata.create_all(engine)
四、增删改查
db = SessionLocal()
# 增
user = User(name="小明", email="xm@example.com", age=25)
db.add(user)
db.commit()
# 查
user = db.get(User, 1) # 按主键查
users = db.query(User).filter(User.age > 18).all() # 条件查
first = db.query(User).filter(User.name == "小明").first()
# 改
user = db.get(User, 1)
user.age = 26
db.commit()
# 删
db.delete(user)
db.commit()
db.close()
五、关联查询
# 查询用户的所有文章
user = db.get(User, 1)
for post in user.posts:
print(post.title)
# 查询文章的作者
post = db.get(Post, 1)
print(post.author.name)
# 联表查询:查所有文章及其作者名
from sqlalchemy.orm import joinedload
posts = db.query(Post).options(joinedload(Post.author)).all()
for p in posts:
print(p.title, p.author.name)
六、高级查询
from sqlalchemy import func
# 聚合:按年龄分组统计人数
result = db.query(User.age, func.count(User.id)).group_by(User.age).all()
# 排序 + 分页
page = db.query(User).order_by(User.id.desc()).offset(0).limit(10).all()
# 模糊查询
search = db.query(User).filter(User.name.like("%明%")).all()
# 原生 SQL(复杂查询时)
from sqlalchemy import text
result = db.execute(text("SELECT * FROM users WHERE age > :age"), {"age": 18}).all()
七、总结
SQLAlchemy ORM 让数据库操作变成面向对象的方式:定义模型类代替手写建表 SQL,用 query API 代替字符串拼接查询。关联查询通过 relationship 自动处理 JOIN。记住:简单查询用 ORM,复杂查询可以混合原生 SQL,两者不冲突。