作为一名长期使用Python进行全栈开发的工程师,我深刻体会到数据库操作在项目中的重要性。SQLAlchemy作为Python生态中最强大的ORM工具之一,几乎成为了我的日常开发标配。今天,我将结合多年实战经验,带你深入掌握这个工具的核心用法。
SQLAlchemy不仅仅是一个ORM框架,它实际上提供了两套操作数据库的API:核心SQL表达式语言和ORM层。ORM层建立在核心层之上,提供了更加面向对象的数据库操作方式。对于大多数应用场景,ORM已经足够强大且易于使用。它能让你用Python类表示数据库表,用对象属性表示字段,大大简化了数据库操作。
安装SQLAlchemy非常简单,使用pip即可完成:
bash复制pip install sqlalchemy
但实际项目中,我们通常还需要安装特定数据库的驱动。不同数据库的驱动选择很有讲究:
提示:生产环境中,建议固定依赖版本以避免兼容性问题,如:
pip install sqlalchemy==1.4.46 psycopg2-binary==2.9.5
创建数据库引擎是使用SQLAlchemy的第一步:
python复制from sqlalchemy import create_engine
# 基础配置
engine = create_engine(
"postgresql://user:password@localhost:5432/mydb",
echo=True, # 开发时开启,方便查看SQL
pool_size=5, # 连接池大小
max_overflow=10, # 允许超出pool_size的连接数
pool_timeout=30, # 获取连接超时时间(秒)
pool_recycle=3600 # 连接回收时间(秒)
)
连接池配置对性能影响很大。根据我的经验:
SQLAlchemy使用declarative_base()创建模型基类:
python复制from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(64), unique=True, nullable=False)
email = Column(String(120), unique=True)
created_at = Column(DateTime, server_default=func.now())
字段类型的选择需要考虑:
python复制class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(100))
user_id = Column(Integer, ForeignKey('users.id'))
# 定义关系
author = relationship("User", back_populates="posts")
# 在User类中添加反向引用
User.posts = relationship("Post", back_populates="author", cascade="all, delete-orphan")
cascade参数控制级联操作:
python复制# 关联表
post_tags = Table('post_tags', Base.metadata,
Column('post_id', Integer, ForeignKey('posts.id')),
Column('tag_id', Integer, ForeignKey('tags.id'))
)
class Tag(Base):
__tablename__ = 'tags'
id = Column(Integer, primary_key=True)
name = Column(String(30), unique=True)
posts = relationship("Post", secondary=post_tags, back_populates="tags")
# 在Post类中添加
Post.tags = relationship("Tag", secondary=post_tags, back_populates="posts")
python复制from sqlalchemy.orm import sessionmaker
SessionLocal = sessionmaker(bind=engine)
# 最佳实践:使用上下文管理器
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
Web框架集成示例(FastAPI):
python复制@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404)
return user
python复制# 自动提交模式(不推荐)
try:
db.add(user)
db.commit()
except:
db.rollback()
raise
# 嵌套事务
with db.begin_nested():
db.add(log_entry)
# 保存点
savepoint = db.begin_nested()
try:
db.execute(some_query)
savepoint.commit()
except:
savepoint.rollback()
python复制# 避免SELECT * - 只查询需要的列
users = db.query(User.id, User.name).all()
# 使用yield_per处理大数据集
for user in db.query(User).yield_per(100):
process_user(user)
python复制from sqlalchemy import or_, and_, not_
# 复杂条件组合
query = db.query(User).filter(
or_(
User.name.like('张%'),
and_(
User.age >= 18,
User.age <= 30
)
)
)
# 子查询
subq = db.query(Post.user_id).filter(Post.created_at > last_week).subquery()
active_users = db.query(User).filter(User.id.in_(subq))
python复制from sqlalchemy.orm import joinedload, subqueryload
# 避免N+1查询
# 方法1:joinedload(适合一对一、多对一)
users = db.query(User).options(joinedload(User.posts)).all()
# 方法2:subqueryload(适合一对多)
users = db.query(User).options(subqueryload(User.posts)).all()
# 多级加载
result = db.query(User).options(
joinedload(User.posts).subqueryload(Post.tags)
).all()
python复制# 批量插入(比逐条插入快10-100倍)
db.bulk_insert_mappings(User, [
{'name': 'user1', 'email': 'user1@test.com'},
{'name': 'user2', 'email': 'user2@test.com'}
])
# 批量更新
db.bulk_update_mappings(User, [
{'id': 1, 'name': 'new_name1'},
{'id': 2, 'name': 'new_name2'}
])
python复制from sqlalchemy import event
@event.listens_for(engine, 'checkout')
def on_checkout(dbapi_conn, connection_record, connection_proxy):
print(f"Checkout event: {connection_record.info}")
@event.listens_for(engine, 'checkin')
def on_checkin(dbapi_conn, connection_record):
print(f"Checkin event: {connection_record.info}")
python复制# 检测游离对象
if db.is_modified(user):
db.commit()
# 处理过期对象
db.refresh(user)
python复制# 在请求结束时检查
if db.in_transaction():
db.rollback()
logger.error("Uncommitted transaction detected")
python复制# 读写分离配置
read_engines = [
create_engine("postgresql://readonly@replica1/db"),
create_engine("postgresql://readonly@replica2/db")
]
class RoutingSession(Session):
def get_bind(self, mapper=None, clause=None):
if self._flushing: # 写操作使用主库
return master_engine
return random.choice(read_engines)
经过多年实践,我发现SQLAlchemy最强大的地方在于它的灵活性。它既提供了高级的ORM抽象,又允许你在需要时直接使用原始SQL。掌握好这个工具,能让你在Python数据库编程中游刃有余。