Python高级技术之:`SQLAlchemy`的`ORM`和`Core`:在不同场景下的选型。

各位观众老爷,大家好! 欢迎来到今天的技术讲座,我是你们的老朋友,今天咱们聊点硬核的——Python的SQLAlchemy。 没错,就是那个让你又爱又恨,能把数据库操作玩出花的 SQLAlchemy!

今天的主题是: SQLAlchemy的ORM和Core:在不同场景下的选型

简单来说,SQLAlchemy就像一个变形金刚,有两种形态:ORM(对象关系映射)和Core(SQL表达式语言)。 它们都能帮你操作数据库,但侧重点和使用方式完全不同。 什么时候用哪一个? 这就是咱们今天要搞清楚的。

一、 先来个热身: SQLAlchemy 是个啥?

SQLAlchemy 并不是一个数据库,而是一个Python的SQL工具包和ORM(对象关系映射)工具。 它提供了一整套高级工具,让你能用Python代码操作各种数据库,包括但不限于:MySQL、PostgreSQL、SQLite、Oracle等等。

为什么要用 SQLAlchemy? 简单来说,它能解决以下几个问题:

  • 数据库兼容性问题: 不同的数据库,SQL语法和驱动可能有所不同。 SQLAlchemy 帮你屏蔽了这些差异,让你只需编写一套代码,就能在不同的数据库之间切换。
  • SQL 注入问题: SQLAlchemy 使用参数化查询,可以有效防止 SQL 注入攻击,保障你的数据安全。
  • 代码可读性和可维护性问题: 使用 SQLAlchemy,你可以用 Python 对象来表示数据库中的表和数据,让代码更易读、易维护。 特别是ORM,更是将数据库操作抽象成了对象操作,大大提高了开发效率。

二、 变形金刚的两种形态: ORM 和 Core

SQLAlchemy 提供了两种主要的使用方式:

  • ORM (Object Relational Mapper): ORM 是一种编程技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。 在 SQLAlchemy 中,ORM 允许你用 Python 类来表示数据库中的表,用 Python 对象来表示表中的数据。 这样,你就可以像操作 Python 对象一样操作数据库,而无需编写原始的 SQL 语句。
  • Core (SQL Expression Language): Core 是 SQLAlchemy 的底层引擎,它提供了一种用 Python 代码构建 SQL 表达式的方式。 你可以用 Core 来编写更加灵活和精细的 SQL 查询,完全掌控 SQL 的生成过程。

简单比喻一下:

  • ORM: 就像自动挡汽车,你只需要踩油门、刹车,就能轻松驾驶,无需关心复杂的机械原理。 适合快速开发、业务逻辑复杂的场景。
  • Core: 就像手动挡汽车,你需要自己控制离合、油门、换挡,才能完美驾驭。 适合对性能要求极高、需要精细控制 SQL 的场景。

三、 ORM: 对象世界的魔法

ORM 的核心思想是: 将数据库中的表映射到 Python 类,将表中的行映射到 Python 对象。 这样,你就可以用面向对象的方式来操作数据库。

1. 定义模型 (Models):

首先,你需要定义 Python 类,来表示数据库中的表。 这些类被称为 "模型" (Models)。

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

# 创建数据库引擎 (根据你的数据库类型修改连接字符串)
engine = create_engine('sqlite:///:memory:')  # 使用内存数据库,方便演示

# 创建一个基类,所有模型都继承它
Base = declarative_base()

# 定义一个 User 模型
class User(Base):
    __tablename__ = 'users'  # 表名

    id = Column(Integer, primary_key=True)  # 主键
    name = Column(String)
    age = Column(Integer)

    def __repr__(self):
        return f"<User(name='{self.name}', age={self.age})>"

# 创建所有表 (基于模型)
Base.metadata.create_all(engine)

# 创建一个会话 (Session) 类,用于与数据库交互
Session = sessionmaker(bind=engine)
session = Session()

代码解释:

  • create_engine(): 创建数据库引擎,指定数据库连接字符串。 这里使用了 SQLite 的内存数据库,方便演示。 你需要根据自己的数据库类型修改连接字符串。 例如,MySQL 的连接字符串可能是:'mysql+pymysql://user:password@host:port/database'
  • declarative_base(): 创建一个基类,所有模型都继承它。 这个基类提供了 ORM 所需的一些基本功能。
  • User(Base): 定义一个 User 模型,继承自 Base
  • __tablename__ = 'users': 指定模型对应的表名。
  • Column(Integer, primary_key=True): 定义一个整数类型的列,作为主键。
  • Column(String): 定义一个字符串类型的列。
  • __repr__(): 定义模型的字符串表示形式,方便调试。
  • Base.metadata.create_all(engine): 创建所有表 (基于模型)。
  • sessionmaker(bind=engine): 创建一个会话类,用于与数据库交互。
  • session = Session(): 创建一个会话对象。

2. 增删改查 (CRUD):

有了模型,就可以进行增删改查操作了。

# 创建用户
user1 = User(name='Alice', age=30)
user2 = User(name='Bob', age=25)

# 添加到会话
session.add(user1)
session.add(user2)

# 提交会话 (保存到数据库)
session.commit()

# 查询用户
users = session.query(User).all()
print(users)  # 输出: [<User(name='Alice', age=30)>, <User(name='Bob', age=25)>]

# 根据 ID 查询用户
user = session.query(User).filter(User.id == 1).first()
print(user)  # 输出: <User(name='Alice', age=30)>

# 修改用户
user.age = 31
session.commit()

# 删除用户
session.delete(user)
session.commit()

# 再次查询用户
users = session.query(User).all()
print(users)  # 输出: [<User(name='Bob', age=25)>]

# 关闭会话
session.close()

代码解释:

  • session.add(user): 将用户对象添加到会话中。 会话会跟踪所有添加、修改、删除的对象,直到你提交会话。
  • session.commit(): 提交会话,将所有修改保存到数据库。
  • session.query(User).all(): 查询所有用户。
  • session.query(User).filter(User.id == 1).first(): 根据 ID 查询第一个用户。
  • user.age = 31: 修改用户对象的属性。
  • session.delete(user): 从会话中删除用户对象。
  • session.close(): 关闭会话。 释放资源。

3. 关系 (Relationships):

ORM 的一个强大之处在于,它可以处理表之间的关系,比如一对一、一对多、多对多。

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

class Address(Base):
    __tablename__ = 'addresses'

    id = Column(Integer, primary_key=True)
    email_address = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey('users.id'))  # 外键关联到 users 表的 id 列

    user = relationship("User", back_populates="addresses")  # 定义与 User 模型的关系

    def __repr__(self):
        return f"<Address(email_address='{self.email_address}')>"

User.addresses = relationship("Address", back_populates="user")  # 在 User 模型中定义与 Address 模型的关系

代码解释:

  • ForeignKey('users.id'): 定义一个外键,关联到 users 表的 id 列。
  • relationship("User", back_populates="addresses"): 定义与 User 模型的关系。 back_populates 参数用于在 User 模型中建立反向关系。
  • User.addresses = relationship("Address", back_populates="user"): 在 User 模型中定义与 Address 模型的关系。

有了关系,你就可以轻松地访问关联数据了。

# 创建用户和地址
user = User(name='Charlie', age=40)
address1 = Address(email_address='[email protected]', user=user)
address2 = Address(email_address='[email protected]', user=user)

# 添加到会话
session.add(user)
session.add(address1)
session.add(address2)

# 提交会话
session.commit()

# 查询用户及其地址
user = session.query(User).filter(User.name == 'Charlie').first()
print(user.addresses)  # 输出: [<Address(email_address='[email protected]')>, <Address(email_address='[email protected]')>]

# 查询地址及其用户
address = session.query(Address).filter(Address.email_address == '[email protected]').first()
print(address.user)  # 输出: <User(name='Charlie', age=40)>

ORM 的优点:

  • 开发效率高: 用 Python 对象操作数据库,无需编写原始 SQL 语句。
  • 代码可读性好: 面向对象的设计,代码更易读、易维护。
  • 数据库兼容性好: ORM 帮你屏蔽了数据库差异,可以轻松切换数据库。
  • 安全性高: ORM 使用参数化查询,可以有效防止 SQL 注入攻击。

ORM 的缺点:

  • 性能可能较低: ORM 需要将 Python 对象转换成 SQL 语句,可能会带来一定的性能开销。
  • 灵活性较差: ORM 封装了很多底层细节,可能无法满足一些复杂的 SQL 查询需求。

四、 Core: SQL 的掌控者

Core 允许你用 Python 代码构建 SQL 表达式,完全掌控 SQL 的生成过程。 这意味着你可以编写更加灵活和精细的 SQL 查询,但同时也需要更多的代码和对 SQL 的理解。

1. 定义元数据 (Metadata):

首先,你需要定义表的元数据,包括表名、列名、数据类型等等。

from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String

# 创建数据库引擎 (根据你的数据库类型修改连接字符串)
engine = create_engine('sqlite:///:memory:')

# 创建元数据对象
metadata = MetaData()

# 定义 users 表
users_table = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String),
    Column('age', Integer)
)

# 定义 addresses 表
addresses_table = Table('addresses', metadata,
    Column('id', Integer, primary_key=True),
    Column('email_address', String, nullable=False),
    Column('user_id', Integer)  # 没有使用 ForeignKey,因为 Core 更注重底层控制
)

# 创建所有表
metadata.create_all(engine)

# 连接数据库
connection = engine.connect()

代码解释:

  • MetaData(): 创建元数据对象,用于存储表的元数据。
  • Table('users', metadata, ...): 定义一个 users 表,指定表名、元数据对象、列信息。
  • Column('id', Integer, primary_key=True): 定义一个整数类型的列,作为主键。
  • metadata.create_all(engine): 创建所有表 (基于元数据)。
  • engine.connect(): 连接数据库。

2. 增删改查 (CRUD):

有了元数据,就可以进行增删改查操作了。

from sqlalchemy import insert, select, update, delete

# 插入数据
insert_stmt = insert(users_table).values(name='Alice', age=30)
connection.execute(insert_stmt)

insert_stmt = insert(users_table).values(name='Bob', age=25)
connection.execute(insert_stmt)

# 查询数据
select_stmt = select(users_table)
result = connection.execute(select_stmt)
for row in result:
    print(row)  # 输出: (1, 'Alice', 30), (2, 'Bob', 25)

# 根据 ID 查询数据
select_stmt = select(users_table).where(users_table.c.id == 1)
result = connection.execute(select_stmt)
row = result.fetchone()
print(row)  # 输出: (1, 'Alice', 30)

# 修改数据
update_stmt = update(users_table).where(users_table.c.id == 1).values(age=31)
connection.execute(update_stmt)

# 删除数据
delete_stmt = delete(users_table).where(users_table.c.id == 1)
connection.execute(delete_stmt)

# 关闭连接
connection.close()

代码解释:

  • insert(users_table).values(name='Alice', age=30): 创建一个插入语句,插入一条数据。
  • select(users_table): 创建一个查询语句,查询所有数据。
  • select(users_table).where(users_table.c.id == 1): 创建一个查询语句,根据 ID 查询数据。 users_table.c.id 表示 users 表的 id 列。
  • update(users_table).where(users_table.c.id == 1).values(age=31): 创建一个更新语句,根据 ID 修改数据。
  • delete(users_table).where(users_table.c.id == 1): 创建一个删除语句,根据 ID 删除数据。
  • connection.execute(stmt): 执行 SQL 语句。
  • result.fetchone(): 获取查询结果的第一行。

3. 连接 (Joins):

Core 可以轻松地进行表连接操作。

from sqlalchemy import join

# 创建 users 和 addresses 表的连接
join_stmt = join(users_table, addresses_table, users_table.c.id == addresses_table.c.user_id)

# 查询用户及其地址
select_stmt = select(users_table.c.name, addresses_table.c.email_address).select_from(join_stmt)
result = connection.execute(select_stmt)
for row in result:
    print(row)

Core 的优点:

  • 性能高: Core 直接生成 SQL 语句,没有 ORM 的转换开销。
  • 灵活性高: 可以编写任意复杂的 SQL 查询,完全掌控 SQL 的生成过程。
  • 更接近底层: 可以更好地理解 SQL 的执行过程,方便进行性能优化。

Core 的缺点:

  • 开发效率低: 需要编写大量的 SQL 代码,开发效率较低。
  • 代码可读性差: SQL 代码相对冗长,可读性较差。
  • 需要更多的 SQL 知识: 需要对 SQL 语法和数据库原理有深入的了解。

五、 选型指南: 什么时候用 ORM,什么时候用 Core?

特性 ORM Core
开发效率
代码可读性
性能 相对较低
灵活性 相对较差
SQL 知识要求 较低 较高
适用场景 快速开发、业务逻辑复杂的场景、对性能要求不高的场景 对性能要求极高、需要精细控制 SQL 的场景、需要进行复杂 SQL 优化的场景

总结:

  • ORM: 适合快速开发、业务逻辑复杂的 Web 应用。 例如,开发一个博客系统、电商网站、社交平台等等。
  • Core: 适合对性能要求极高的数据分析、ETL (Extract, Transform, Load) 任务。 例如,处理海量数据、进行复杂的统计分析等等。

更细致的建议:

  • 如果你是新手,或者项目对开发效率要求很高,那么 ORM 是一个不错的选择。 它可以让你快速上手,用更少的代码完成更多的功能。
  • 如果你的项目对性能要求极高,或者需要进行复杂的 SQL 优化,那么 Core 是一个更好的选择。 它可以让你完全掌控 SQL 的生成过程,从而实现更高的性能。
  • 在实际项目中,你也可以将 ORM 和 Core 结合使用。 对于简单的 CRUD 操作,可以使用 ORM。 对于复杂的 SQL 查询,可以使用 Core。

六、 混合使用 ORM 和 Core

SQLAlchemy 允许你将 ORM 和 Core 结合使用,充分发挥两者的优势。

# 使用 ORM 定义模型
class Product(Base):
    __tablename__ = 'products'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    price = Column(Integer)

    def __repr__(self):
        return f"<Product(name='{self.name}', price={self.price})>"

# 创建表
Base.metadata.create_all(engine)

# 使用 Core 编写 SQL 查询
from sqlalchemy import func

# 统计所有产品的平均价格
stmt = select(func.avg(Product.__table__.c.price))
result = session.execute(stmt)
average_price = result.scalar()  # 使用 scalar() 获取单个标量值

print(f"Average price: {average_price}")

代码解释:

  • Product.__table__: 获取 Product 模型对应的表对象。
  • func.avg(Product.__table__.c.price): 使用 func 模块的 avg 函数,计算 products 表的 price 列的平均值。
  • result.scalar(): 从查询结果中获取单个标量值。

七、 最后的小贴士

  • 学习 SQLAlchemy 的官方文档: 官方文档是最好的学习资料,里面包含了 SQLAlchemy 的所有功能和用法。
  • 多做实验: 实践是最好的老师,多写代码,多调试,才能真正掌握 SQLAlchemy。
  • 关注 SQLAlchemy 的社区: SQLAlchemy 有一个活跃的社区,你可以在社区里提问、交流经验。

好了,今天的讲座就到这里。 希望大家能够对 SQLAlchemy 的 ORM 和 Core 有更深入的了解,并在实际项目中灵活运用。 谢谢大家! 下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注