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打印出来。我刚开始学的时候一直开着它,方便调试。等熟悉了再关掉,不然日志会刷屏。

我的经验:Engine是全局唯一的,一个应用通常只创建一个Engine实例。别在每次请求时都新建,那样会浪费资源。

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
注意:Session不是线程安全的。多线程环境下,每个线程应该创建自己的Session实例。

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 知识体系总览

为了让你更直观地理解这三个核心概念的关系,我画了张图:

SQLAlchemy 核心架构图 Engine 连接数据库 管理连接池 执行原始SQL Session 事务管理 增删改查操作 对象状态跟踪 Model 定义表结构 字段与约束 关联关系 提供连接 操作对象 工作流程 1. 定义 Model 类,描述数据库表结构 2. 创建 Engine,连接到目标数据库 3. 通过 Session 操作 Model 对象,Engine 自动执行 SQL

4.4 避坑指南

最后,分享几个我踩过的坑:

  • 字段类型要匹配:我曾经用Integer存手机号,结果前面带0的号码全丢了。字符串就是字符串,别偷懒。
  • 外键约束别滥用:在ORM里定义外键很方便,但生产环境高并发下,外键约束可能成为性能瓶颈。我一般只在核心表之间用外键。
  • Session的生命周期:记住一个原则——谁创建谁关闭。别在函数A里创建Session,却在函数B里关闭,容易出问题。
  • 延迟加载要注意:默认情况下,关联对象是延迟加载的。如果你在Session关闭后访问关联属性,会报错。要么用joinedload提前加载,要么确保在Session内访问。
小技巧:调试时可以用 session.query(Model).statement 查看生成的SQL语句,方便排查问题。

好了,ORM入门就聊到这儿。记住这三个核心概念,后面的路就好走了。你想想看,用Python对象操作数据库,是不是比手写SQL舒服多了?


专注资料整理