4. ORM入门:SQLAlchemy核心概念
说实话,我第一次接触ORM的时候,心里是有点抵触的。那时候我习惯手写SQL,觉得ORM就是个中间商,多了一层反而麻烦。直到有一次接手一个老项目,里面光数据库表就有三十多张,手写SQL的代码散落得到处都是,改个字段名要翻遍整个项目……嗯,从那以后,我彻底倒向了ORM。
今天咱们聊的SQLAlchemy,是Python世界里最成熟的ORM框架之一。它不是什么花架子,而是真正能帮你把数据库操作变得优雅的工具。我个人习惯把它比作「翻译官」——你写Python对象,它帮你翻译成SQL语句。
4.1 三个核心概念:Engine, Session, Model
要理解SQLAlchemy,先搞明白这三个东西。它们就像三根柱子,撑起了整个ORM体系。
4.1.1 Engine —— 数据库的「门卫」
Engine负责跟数据库建立连接。说白了,它就是数据库的入口。你告诉它数据库在哪、用户名密码是什么,它帮你管好连接池、执行SQL。
from sqlalchemy import create_engine
# 连接SQLite数据库(文件形式)
engine = create_engine('sqlite:///myblog.db', echo=True)
# 连接MySQL(实际项目中常用)
# engine = create_engine('mysql+pymysql://user:password@localhost/mydb')
这里有个小细节:echo=True 会把生成的SQL打印出来。我刚开始学的时候一直开着它,方便调试。等熟悉了再关掉,不然日志会刷屏。
4.1.2 Session —— 数据库的「工作台」
Session是你跟数据库打交道的直接对象。你用它来增删改查,它帮你管理事务。每次操作完,记得提交或回滚。
from sqlalchemy.orm import sessionmaker
# 创建Session类
Session = sessionmaker(bind=engine)
# 创建Session实例
session = Session()
# 使用Session操作数据库
# ... 各种操作 ...
# 提交事务
session.commit()
# 用完关闭
session.close()
我曾经犯过一个低级错误:开了Session忘了关,导致连接池被占满,数据库直接罢工。所以我现在习惯用上下文管理器:
from contextlib import contextmanager
@contextmanager
def get_session():
session = Session()
try:
yield session
session.commit()
except:
session.rollback()
raise
finally:
session.close()
# 使用
with get_session() as session:
# 在这里操作数据库
pass
4.1.3 Model —— 数据库表的「蓝图」
Model就是Python类,它对应数据库中的一张表。你定义好类,SQLAlchemy帮你创建表结构。
from sqlalchemy import Column, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
# 创建基类
Base = declarative_base()
# 定义模型类
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
username = Column(String(50), unique=True, nullable=False)
email = Column(String(100), unique=True)
created_at = Column(DateTime, default=datetime.utcnow)
def __repr__(self):
return f'<User(id={self.id}, username={self.username})>'
你看,每个类属性对应一个数据库字段。类型、约束、默认值,全都在这里定义。这就是ORM的魅力——用Python的语法描述数据库结构。
4.2 定义模型类:从理论到实践
光说不练假把式。咱们用一个博客系统的例子,把模型类定义讲透。
4.2.1 基础字段类型
SQLAlchemy支持很多字段类型,常用的有这些:
| Python类型 | SQLAlchemy类型 | 说明 |
|---|---|---|
| int | Integer | 整数 |
| float | Float | 浮点数 |
| str | String(length) | 字符串,需指定长度 |
| bool | Boolean | 布尔值 |
| datetime | DateTime | 日期时间 |
| text | Text | 长文本 |
4.2.2 完整示例:博客模型
咱们定义一个完整的博客模型,包含用户、文章和评论:
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base
from datetime import datetime
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), unique=True, nullable=False)
password_hash = Column(String(128), nullable=False)
email = Column(String(100))
bio = Column(Text, default='')
created_at = Column(DateTime, default=datetime.utcnow)
# 关联关系
posts = relationship('Post', back_populates='author')
def __repr__(self):
return f'<User {self.username}>'
class Post(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True)
title = Column(String(200), nullable=False)
content = Column(Text, nullable=False)
published = Column(Boolean, default=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, onupdate=datetime.utcnow)
# 外键
user_id = Column(Integer, ForeignKey('users.id'))
# 关联关系
author = relationship('User', back_populates='posts')
comments = relationship('Comment', back_populates='post', cascade='all, delete-orphan')
class Comment(Base):
__tablename__ = 'comments'
id = Column(Integer, primary_key=True)
content = Column(Text, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
# 外键
post_id = Column(Integer, ForeignKey('posts.id'))
user_id = Column(Integer, ForeignKey('users.id'))
# 关联关系
post = relationship('Post', back_populates='comments')
author = relationship('User')
ForeignKey定义外键,指向另一张表的字段relationship定义对象之间的关联,方便通过Python对象导航back_populates让两个方向的关系互相感知cascade='all, delete-orphan'表示删除文章时,自动删除关联的评论
4.2.3 创建表
模型定义好了,怎么让它变成真实的数据库表?很简单:
# 创建所有表
Base.metadata.create_all(engine)
# 如果只想创建某个模型对应的表
User.__table__.create(engine)
我第一次用的时候,以为要手动写CREATE TABLE语句。结果一行代码搞定,当时就觉得:嗯,真香。
4.3 知识体系总览
为了让你更直观地理解这三个核心概念的关系,我画了张图:
4.4 避坑指南
最后,分享几个我踩过的坑:
- 字段类型要匹配:我曾经用Integer存手机号,结果前面带0的号码全丢了。字符串就是字符串,别偷懒。
- 外键约束别滥用:在ORM里定义外键很方便,但生产环境高并发下,外键约束可能成为性能瓶颈。我一般只在核心表之间用外键。
- Session的生命周期:记住一个原则——谁创建谁关闭。别在函数A里创建Session,却在函数B里关闭,容易出问题。
- 延迟加载要注意:默认情况下,关联对象是延迟加载的。如果你在Session关闭后访问关联属性,会报错。要么用
joinedload提前加载,要么确保在Session内访问。
session.query(Model).statement 查看生成的SQL语句,方便排查问题。
好了,ORM入门就聊到这儿。记住这三个核心概念,后面的路就好走了。你想想看,用Python对象操作数据库,是不是比手写SQL舒服多了?