SQLAlchemy 技术研究性教程(一)

SQLAlchemy入门与实战指南

为面向对象编程思维而生的数据库优雅访问:SQLAlchemy 

本文章仅提供学习,切勿将其用于不法手段!

——从“数据库小白”到“ORM 老司机”的进阶之路

前言:为什么学 SQLAlchemy?

你有没有过这种经历:写 Python 操作数据库时,一会儿拼 SQL 字符串(比如 SELECT * FROM user WHERE id=1),一会儿处理各种数据库差异(MySQL 用 AUTO_INCREMENT,PostgreSQL 用 SERIAL),改个表结构要手动改 N 处代码……累得头都大了?

这时候,​SQLAlchemy​ 就像个“数据库管家”——它帮你把 Python 对象和数据库表“绑”在一起(这叫 ORM,对象关系映射),让你用写 Python 类的方式操作数据库,不用再和 SQL 字符串死磕。而且它支持几乎所有主流数据库(MySQL、PostgreSQL、SQLite 等),切换数据库时几乎不用改业务代码!

本教程会从“最基础的安装”开始,用大白话+生活例子,一步步带你搞懂 SQLAlchemy 的核心玩法,最后还能玩点高级操作。放心,就算你是数据库新手,也能看懂!


第一章:先搭个“实验室”——安装与环境准备

1.1 安装 SQLAlchemy

首先,你得把 SQLAlchemy 装到电脑里。打开终端(Windows 叫“命令提示符”或“PowerShell”),输入:

pip install sqlalchemy  

如果要用 MySQL,还得装个“驱动”(相当于翻译官,让 SQLAlchemy 能和 MySQL 说话):

pip install pymysql  # MySQL 驱动(其他数据库用不同驱动,比如 psycopg2 对应 PostgreSQL)  

1.2 准备一个“测试数据库”

为了安全,咱们先用 SQLite(文件型数据库,不用单独装服务,适合练手)。新建一个文件夹(比如叫 sqlalchemy-tutorial),在里面放个空文件 test.db(这就是我们的“数据库文件”)。

如果用 MySQL,需要先启动 MySQL 服务,然后创建一个测试库(比如叫 test_db):

CREATE DATABASE test_db;  

1.3 验证安装:连个数据库试试

先写段代码,看看能不能连上数据库。新建 demo.py

from sqlalchemy import create_engine  

# 连接 SQLite(注意:sqlite:/// 后面跟的是文件路径,三个斜杠表示相对路径)  
engine = create_engine("sqlite:///test.db")  

# 测试连接:执行一个简单的 SQL(创建表)  
with engine.connect() as conn:  
    conn.execute(text("CREATE TABLE IF NOT EXISTS test (id INT, name TEXT)"))  
    print("数据库连接成功!表 test 已创建(如果不存在的话)")  

运行 python demo.py,如果没报错且打印“数据库连接成功”,说明环境 OK 了!

本章重点​:

  • SQLAlchemy 安装用 pip,MySQL 需额外装驱动(如 pymysql)。
  • create_engine 是“连接器”,负责和数据库建立连接(类似“电话线”)。

第二章:ORM 入门——用 Python 类“变”出数据库表

2.1 什么是 ORM?“对象”和“表”怎么绑一起?

假设你有个“用户”的概念:每个用户有 id(编号)、name(姓名)、age(年龄)。用普通方式存数据库,你得手动建表、插数据;但用 ORM,你可以定义一个 Python 类 User,然后 SQLAlchemy 自动帮你生成对应的数据库表!

类比​:类是“设计图纸”,表是“按图纸盖的房子”。你改图纸(类),房子(表)的结构也会跟着变(当然需要迁移工具,后面会讲)。

2.2 定义第一个 ORM 类:User 表

修改 demo.py,引入 ORM 需要的工具:

from sqlalchemy import create_engine, Column, Integer, String  
from sqlalchemy.ext.declarative import declarative_base  
from sqlalchemy.orm import sessionmaker  

# 第一步:创建“基类”(所有 ORM 类的爸爸)  
Base = declarative_base()  

# 第二步:定义 User 类(对应数据库中的 user 表)  
class User(Base):  
    # 表名(如果不写,默认是类名小写)  
    __tablename__ = "user"  

    # 字段定义(列):类型要和数据库对应  
    id = Column(Integer, primary_key=True, autoincrement=True)  # 主键,自增  
    name = Column(String(50), nullable=False)  # 字符串,最长50,不能为空  
    age = Column(Integer)  # 整数,可以为空  

    def __repr__(self):  
        return f"<User(id={self.id}, name='{self.name}', age={self.age})>"  # 方便打印看结果  

2.3 让类“变成”表:创建表结构

定义了类还不够,得告诉数据库“按这个类建表”。用 Base.metadata.create_all(engine)

# 接上面的代码  
engine = create_engine("sqlite:///test.db")  

# 第三步:根据 Base 的所有子类(比如 User)创建表(如果不存在)  
Base.metadata.create_all(engine)  
print("User 表已创建!")  

运行后,打开 test.db(可以用 SQLite 浏览器工具查看),会发现多了个 user 表,字段和 User 类定义的一致!

本章重点​:

  • declarative_base() 生成 ORM 基类,所有表类都要继承它。
  • __tablename__ 指定表名,Column 定义字段(类型、约束如 primary_key)。
  • Base.metadata.create_all(engine) 是“建表指令”,只在表不存在时创建。

第三章:增删改查(CRUD)——用 Python 代码操作数据库

表建好了,接下来就是最常用的“增删改查”。SQLAlchemy 用 Session(会话)管理操作,可以理解为“临时工作台”:你把要做的操作(增删改)放到工作台上,最后一起提交给数据库。

3.1 先搞个“工作台”:创建 Session

在之前的代码基础上,加一段创建 Session 的配置:

# 接前面的代码(engine 已定义)  
# 创建 Session 工厂(类似“工作台模板”)  
Session = sessionmaker(bind=engine)  

# 每次操作前,生成一个具体的 Session(工作台)  
session = Session()  

3.2 增加(Create):插入新用户

往表里加数据,就像“new 一个对象,然后告诉 Session 保存它”:

# 新增单个用户  
user1 = User(name="小明", age=20)  
session.add(user1)  # 把 user1 放到“待提交区”  

# 新增多个用户(批量添加)  
user2 = User(name="小红", age=22)  
user3 = User(name="小刚", age=21)  
session.add_all([user2, user3])  # 批量添加到“待提交区”  

# 提交操作(把“待提交区”的内容真正写入数据库)  
session.commit()  

print("新增用户成功!当前用户:")  
print(session.query(User).all())  # 查询所有用户看看  

运行后,数据库里会有 3 条记录。session.commit() 是关键——没提交的话,数据只在“内存”里,没进数据库!

3.3 查询(Read):找你想要的数据

查数据是最高频的操作,SQLAlchemy 提供了多种查询方式,像“点菜”一样灵活:

(1)查所有:query().all()
all_users = session.query(User).all()  # 返回所有 User 对象的列表  
print(all_users)  # [<User(id=1, name='小明', age=20)>, ...]  
(2)查单个:query().first()get()
# 按条件查第一个匹配的(比如 name='小明')  
user = session.query(User).filter_by(name="小明").first()  
print(user)  # <User(id=1, name='小明', age=20)>  

# 按主键查(id=1)  
user = session.get(User, 1)  # 更高效,直接定位主键  
print(user)  
(3)复杂条件:filter()(比 filter_by 更强大)

filter_by 只能传“键值对”(比如 name="小明"),而 filter 可以用表达式(比如 age > 20):

# 查年龄大于20的用户  
users = session.query(User).filter(User.age > 20).all()  
print(users)  # [<User(id=2, name='小红', age=22)>, <User(id=3, name='小刚', age=21)>]  

# 多条件(年龄>20 且 姓名含“红”)  
users = session.query(User).filter(User.age > 20, User.name.contains("红")).all()  
print(users)  # [<User(id=2, name='小红', age=22)>]  

3.4 修改(Update):改用户信息

改数据有两种思路:​先查出来改,再提交​ 或 ​直接批量改

方式1:查出来改(适合改单条)
# 先找到要改的用户  
user = session.get(User, 1)  
if user:  
    user.age = 21  # 直接改对象的属性(Python 对象的特性)  
    session.commit()  # 提交修改  
    print("修改成功!")  
方式2:批量改(适合改多条)
# 把所有年龄=21的用户,年龄改成22  
session.query(User).filter(User.age == 21).update({"age": 22})  
session.commit()  
print("批量修改成功!")  

3.5 删除(Delete):删掉不需要的数据

和改数据类似,也是“先查后删”或“直接批量删”。

方式1:查出来删(适合删单条)
user = session.get(User, 3)  
if user:  
    session.delete(user)  # 标记删除  
    session.commit()  # 提交删除  
    print("删除成功!")  
方式2:批量删
# 删掉所有年龄>22的用户(这里示例数据可能没有,所以不会删任何东西)  
session.query(User).filter(User.age > 22).delete()  
session.commit()  
print("批量删除成功!")  

本章重点​:

  • Session 是操作数据库的“工作台”,add/add_all 增,query 查,delete 删,commit 提交。
  • 查数据时,filter_by 简单条件,filter 复杂条件;all() 返回列表,first() 返回单个对象。

第四章:关系映射——表和表之间怎么“关联”?

实际业务中,表很少孤立存在。比如“用户”和“订单”是一对多(一个用户多个订单),“用户”和“角色”是多对多(一个用户多个角色,一个角色多个用户)。SQLAlchemy 能轻松搞定这些关系!

4.1 一对多:用户和订单(一个用户→多个订单)

假设用户表(user)和订单表(order),一个用户可以有多个订单,每个订单属于一个用户。

步骤1:定义 Order 类,添加外键
from sqlalchemy import ForeignKey  

class Order(Base):  
    __tablename__ = "order"  

    id = Column(Integer, primary_key=True, autoincrement=True)  
    order_no = Column(String(50), nullable=False)  # 订单号  
    user_id = Column(Integer, ForeignKey("user.id"))  # 外键:关联到 user 表的 id 字段  

    # 关键:定义“多”的一方(Order)如何访问“一”的一方(User)  
    user = relationship("User", back_populates="orders")  

# 回到 User 类,添加反向引用(一的一方如何访问多的一方)  
class User(Base):  
    __tablename__ = "user"  
    id = Column(Integer, primary_key=True, autoincrement=True)  
    name = Column(String(50), nullable=False)  
    age = Column(Integer)  

    # 新增:一个用户有多个订单(relationship 定义关系)  
    orders = relationship("Order", back_populates="user")  

    def __repr__(self):  
        return f"<User(id={self.id}, name='{self.name}', age={self.age})>"  
步骤2:重新建表(因为加了新表 order
Base.metadata.create_all(engine)  # 会同时检查 user 和 order 表,不存在则创建  
步骤3:用关系操作数据

现在可以通过用户直接访问他的订单,或通过订单访问所属用户:

# 新增用户和订单(自动关联)  
user = User(name="小李", age=25)  
order1 = Order(order_no="ORDER001", user=user)  # 直接把 user 对象传给 user_id  
order2 = Order(order_no="ORDER002", user=user)  

session.add_all([user, order1, order2])  
session.commit()  

# 查用户的订单(正向访问:user → orders)  
print(user.orders)  # [<Order(id=1, order_no='ORDER001', user_id=4)>, ...]  

# 查订单的用户(反向访问:order → user)  
print(order1.user)  # <User(id=4, name='小李', age=25)>  

4.2 多对多:用户和角色(用户←→角色)

多对多需要一个“中间表”来记录关联关系(比如 user_role 表,存 user_idrole_id)。

步骤1:定义 Role 类和中间表
from sqlalchemy import Table  

# 中间表(不需要 ORM 类,直接用 Table 定义)  
user_role = Table(  
    "user_role",  # 表名  
    Base.metadata,  
    Column("user_id", Integer, ForeignKey("user.id"), primary_key=True),  
    Column("role_id", Integer, ForeignKey("role.id"), primary_key=True)  
)  

class Role(Base):  
    __tablename__ = "role"  
    id = Column(Integer, primary_key=True, autoincrement=True)  
    role_name = Column(String(50), nullable=False)  

    # 多对多关系:一个角色被多个用户拥有  
    users = relationship("User", secondary=user_role, back_populates="roles")  

# 回到 User 类,添加多对多关系  
class User(Base):  
    __tablename__ = "user"  
    id = Column(Integer, primary_key=True, autoincrement=True)  
    name = Column(String(50), nullable=False)  
    age = Column(Integer)  

    orders = relationship("Order", back_populates="user")  
    roles = relationship("Role", secondary=user_role, back_populates="users")  # secondary 指定中间表  
步骤2:重新建表(新增 roleuser_role 表)
Base.metadata.create_all(engine)  
步骤3:用多对多关系操作
# 新增角色和用户,并关联  
role_admin = Role(role_name="管理员")  
role_user = Role(role_name="普通用户")  
user = User(name="小王", age=23)  

# 给用户分配角色(直接操作 roles 列表)  
user.roles.extend([role_admin, role_user])  

session.add_all([role_admin, role_user, user])  
session.commit()  

# 查用户的角色(正向访问)  
print(user.roles)  # [<Role(id=1, role_name='管理员')>, <Role(id=2, role_name='普通用户')>]  

# 查角色的用户(反向访问)  
print(role_admin.users)  # [<User(id=5, name='小王', age=23)>]  

本章重点​:

  • 一对多:在“多”的一方加外键,relationship 定义双向访问(back_populates)。
  • 多对多:需要中间表,relationshipsecondary 参数指定中间表。

第五章:高级玩法——让操作更“丝滑”

5.1 事务:要么全成功,要么全失败

比如转账:A 扣钱和 B 加钱必须同时成功或同时失败。SQLAlchemy 的 Session 自带事务,默认 commit 时提交,rollback 时回滚。

try:  
    # 模拟转账:A 给用户1加钱,用户1给用户2转钱(故意写错,测试回滚)  
    user_a = User(name="A", age=30)  
    user_b = User(name="B", age=28)  
    session.add_all([user_a, user_b])  
    session.flush()  # 预提交,获取 id(但不真正写入数据库)  

    # 假设这里有个错误(比如除零)  
    raise ValueError("模拟出错!")  

    session.commit()  # 如果前面出错,这行不会执行  
except Exception as e:  
    print(f"出错了:{e},回滚事务")  
    session.rollback()  # 回滚到操作前的状态  

5.2 原生 SQL:复杂查询用它

ORM 虽好,但遇到特别复杂的 SQL(比如窗口函数、多表联查优化),直接写原生 SQL 更直观。用 text() 包裹 SQL 字符串:

from sqlalchemy import text  

# 执行原生 SELECT  
result = session.execute(text("SELECT * FROM user WHERE age > :age"), {"age": 20})  
for row in result:  
    print(row)  # 每行是一个元组(id, name, age)  

# 执行原生 INSERT(注意防注入,用参数绑定)  
session.execute(text("INSERT INTO user (name, age) VALUES (:name, :age)"), {"name": "小张", "age": 24})  
session.commit()  

5.3 性能优化:避免 N+1 查询问题

当查询一个用户的所有订单时,如果直接循环 user.orders,可能会触发多次查询(N+1 问题:1 次查用户 + N 次查订单)。用 joinedload 预加载关联数据:

from sqlalchemy.orm import joinedload  

# 一次性查出用户和他的所有订单(只发 1 次 JOIN 查询)  
users = session.query(User).options(joinedload(User.orders)).all()  
for user in users:  
    print(user.orders)  # 不会再触发额外查询  

本章重点​:

  • 事务用 try...except...rollback 保证原子性。
  • 复杂 SQL 用 text() 写原生语句(注意参数绑定防注入)。
  • N+1 查询用 joinedload 预加载关联数据,提升性能。

总结:SQLAlchemy 到底强在哪?

到这里,你已经掌握了 SQLAlchemy 的核心技能:用 ORM 类映射表、CRUD 操作、关系映射、事务和性能优化。它的优势总结起来就三点:

  1. 省代码​:不用手写 SQL,用 Python 类和方法就能操作数据库。
  2. 跨数据库​:换数据库(比如从 SQLite 切 MySQL)只需改连接字符串,业务代码不动。
  3. 灵活扩展​:支持原生 SQL、复杂关系、事务等高级功能,能满足大部分业务需求。

下一步可以研究 ​Alembic​(SQLAlchemy 的迁移工具),它能帮你优雅地升级/降级数据库表结构(比如加字段、改类型),彻底告别手动改 SQL 的痛苦!

遇到问题别慌,SQLAlchemy 官方文档(https://docs.sqlalchemy.org)是你的“终极字典”,多翻多练,你也能成为 ORM 老司机!

注:本文仅用于教育目的,实际渗透测试必须获得合法授权。未经授权的黑客行为是违法的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值