SqlAlchemy 2.0 中文文档(二十三)(2)

简介: SqlAlchemy 2.0 中文文档(二十三)

SqlAlchemy 2.0 中文文档(二十三)(1)https://developer.aliyun.com/article/1560514


删除

删除级联表示当“父”对象标记为删除时,其相关的“子”对象也应标记为删除。例如,如果我们有一个配置了删除级联的关系User.addresses

class User(Base):
    # ...
    addresses = relationship("Address", cascade="all, delete")

如果使用上述映射,我们有一个User对象和两个相关的Address对象:

>>> user1 = sess1.scalars(select(User).filter_by(id=1)).first()
>>> address1, address2 = user1.addresses

如果我们标记user1进行删除,在刷新操作进行后,address1address2也将被删除:

>>> sess.delete(user1)
>>> sess.commit()
DELETE  FROM  address  WHERE  address.id  =  ?
((1,),  (2,))
DELETE  FROM  user  WHERE  user.id  =  ?
(1,)
COMMIT 

或者,如果我们的User.addresses关系没有删除级联,SQLAlchemy 的默认行为是通过将它们的外键引用设置为NULL来解除user1address1address2的关联。使用以下映射:

class User(Base):
    # ...
    addresses = relationship("Address")

在删除父User对象时,address中的行不会被删除,而是被解除关联:

>>> sess.delete(user1)
>>> sess.commit()
UPDATE  address  SET  user_id=?  WHERE  address.id  =  ?
(None,  1)
UPDATE  address  SET  user_id=?  WHERE  address.id  =  ?
(None,  2)
DELETE  FROM  user  WHERE  user.id  =  ?
(1,)
COMMIT 

删除 在一对多关系上的级联通常与删除孤儿级联结合使用,如果“子”对象与父对象解除关联,则会发出与相关行相关的 DELETE 操作。删除删除孤儿级联的组合涵盖了 SQLAlchemy 需要在将外键列设置为 NULL 与完全删除行之间做出决定的情况。

默认情况下,该功能完全独立于数据库配置的可能配置CASCADE行为的FOREIGN KEY约束。为了更有效地与此配置集成,应使用在使用 ORM 关系的外键 ON DELETE 级联中描述的附加指令。

警告

请注意,ORM 的“删除”和“删除孤立对象”行为仅适用于使用Session.delete()方法在工作单元过程中标记个别 ORM 实例进行删除。它不适用于“批量”删除,这将使用delete()构造来发出,如 ORM UPDATE and DELETE with Custom WHERE Criteria 中所示。有关更多背景信息,请参见 ORM-启用的更新和删除的重要说明和注意事项。

另见

使用 ORM 关系的外键 ON DELETE 级联

使用多对多关系的级联删除

delete-orphan

使用多对多关系的级联删除

cascade="all, delete"选项与多对多关系同样适用,这种关系使用relationship.secondary来指示一个关联表。当删除父对象时,因此取消与其相关的对象的关联时,工作单元过程通常会从关联表中删除行,但会保留相关的对象。当与cascade="all, delete"组合时,额外的DELETE语句将对子行本身进行操作。

下面的示例调整了多对多的例子,以说明关联的端上的cascade="all, delete"设置:

association_table = Table(
    "association",
    Base.metadata,
    Column("left_id", Integer, ForeignKey("left.id")),
    Column("right_id", Integer, ForeignKey("right.id")),
)
class Parent(Base):
    __tablename__ = "left"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        secondary=association_table,
        back_populates="parents",
        cascade="all, delete",
    )
class Child(Base):
    __tablename__ = "right"
    id = mapped_column(Integer, primary_key=True)
    parents = relationship(
        "Parent",
        secondary=association_table,
        back_populates="children",
    )

当使用Session.delete()标记要删除的Parent对象时,上述情况下,刷新过程通常会从association表中删除关联行,但根据级联规则,它还将删除所有相关的Child行。

警告

如果上述cascade="all, delete"设置在两个关系上都配置了,则级联操作将继续通过所有ParentChild对象,加载遇到的每个childrenparents集合,并删除所有连接的内容。通常不希望将“删除”级联配置为双向。

另见

从多对多表中删除行

使用外键 ON DELETE 与多对多关系 ### 使用 ORM 关系的外键 ON DELETE 级联

SQLAlchemy 的“delete”级联行为与数据库FOREIGN KEY约束的ON DELETE特性重叠。SQLAlchemy 允许使用ForeignKeyForeignKeyConstraint构造配置这些模式级 DDL 行为;与Table元数据一起使用这些对象的用法在 ON UPDATE and ON DELETE 中有描述。

为了在与relationship()一起使用ON DELETE外键级联时,首先需要注意的是relationship.cascade设置仍然必须配置为与所需的delete或“set null”行为匹配(使用delete级联或将其省略),以便 ORM 或数据库级约束将处理实际修改数据库中数据的任务时,ORM 仍然能够适当跟踪可能受影响的本地存在的对象的状态。

然后,在relationship()上有一个附加选项,指示 ORM 应该尝试自己运行 DELETE/UPDATE 操作相关行的程度,还是应该依赖于期望数据库端 FOREIGN KEY 约束级联处理任务;这是relationship.passive_deletes参数,它接受False(默认值),True"all"选项。

最典型的示例是,在删除父行时删除子行,并且在相关的FOREIGN KEY约束上配置了ON DELETE CASCADE

class Parent(Base):
    __tablename__ = "parent"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        back_populates="parent",
        cascade="all, delete",
        passive_deletes=True,
    )
class Child(Base):
    __tablename__ = "child"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("parent.id", ondelete="CASCADE"))
    parent = relationship("Parent", back_populates="children")

当删除父行时,上述配置的行为如下:

  1. 应用程序调用session.delete(my_parent),其中my_parentParent类的一个实例。
  2. Session下次将更改刷新到数据库时,my_parent.children集合中的当前加载的所有项目都将被 ORM 删除,这意味着为每个记录发出一个DELETE语句。
  3. 如果 my_parent.children 集合未加载,则不会发出任何 DELETE 语句。如果在这个 relationship()设置 relationship.passive_deletes 标志,则会发出一个用于未加载的 Child 对象的 SELECT 语句。
  4. 然后为 my_parent 行本身发出一个 DELETE 语句。
  5. 数据库级别的 ON DELETE CASCADE 设置确保了所有引用受影响的 parent 行的 child 行也被删除。
  6. my_parent 引用的 Parent 实例,以及与此对象相关且已经加载(即发生了步骤 2)的所有 Child 实例,都会从 Session 中解除关联。

注意

要使用“ON DELETE CASCADE”,底层数据库引擎必须支持FOREIGN KEY约束,并且它们必须是强制执行的:

  • 当使用 MySQL 时,必须选择适当的存储引擎。详情请参阅包括存储引擎的 CREATE TABLE 参数。
  • 当使用 SQLite 时,必须显式启用外键支持。详情请参阅外键支持。### 使用外键 ON DELETE 处理多对多关系

如 使用级联删除处理多对多关系 中所述,“delete”级联也适用于多对多关系。要使用 ON DELETE CASCADE 外键与多对多一起使用,必须在关联表上配置 FOREIGN KEY 指令。这些指令可以处理自动从关联表中删除,但不能自动删除相关对象本身。

在这种情况下,relationship.passive_deletes 指令可以在删除操作期间为我们节省一些额外的 SELECT 语句,但仍然有一些集合 ORM 将继续加载,以便定位受影响的子对象并正确处理它们。

注意

对此的假设优化可能包括一条针对关联表的所有父关联行的单个 DELETE 语句,然后使用 RETURNING 定位受影响的相关子行,但是这目前不是 ORM 工作单元实现的一部分。

在此配置中,我们在关联表的两个外键约束上都配置了 ON DELETE CASCADE。我们在关系的父->子方向上配置了 cascade="all, delete",然后我们可以在双向关系的另一侧上配置 passive_deletes=True,如下所示:

association_table = Table(
    "association",
    Base.metadata,
    Column("left_id", Integer, ForeignKey("left.id", ondelete="CASCADE")),
    Column("right_id", Integer, ForeignKey("right.id", ondelete="CASCADE")),
)
class Parent(Base):
    __tablename__ = "left"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        secondary=association_table,
        back_populates="parents",
        cascade="all, delete",
    )
class Child(Base):
    __tablename__ = "right"
    id = mapped_column(Integer, primary_key=True)
    parents = relationship(
        "Parent",
        secondary=association_table,
        back_populates="children",
        passive_deletes=True,
    )

使用上述配置,删除 Parent 对象的过程如下:

  1. 使用 Session.delete() 标记要删除的 Parent 对象。
  2. 当刷新发生时,如果未加载 Parent.children 集合,则 ORM 将首先发出 SELECT 语句以加载与 Parent.children 对应的 Child 对象。
  3. 然后会为对应于该父行的 association 中的行发出 DELETE 语句。
  4. 对于由此立即删除受影响的每个 Child 对象,因为配置了 passive_deletes=True,工作单元不需要尝试为每个 Child.parents 集合发出 SELECT 语句,因为假设将删除 association 中对应的行。
  5. 然后对从 Parent.children 加载的每个 Child 对象发出 DELETE 语句。 ### 使用删除级联处理多对多关系

cascade="all, delete" 选项与多对多关系同样有效,即使用 relationship.secondary 指示关联表的关系。当删除父对象并因此取消关联其相关对象时,工作单元进程通常会删除关联表中的行,但保留相关对象。当与 cascade="all, delete" 结合使用时,将为子行本身执行额外的 DELETE 语句。

以下示例将多对多的示例调整为示例,以说明在关联的一侧上设置 cascade="all, delete"

association_table = Table(
    "association",
    Base.metadata,
    Column("left_id", Integer, ForeignKey("left.id")),
    Column("right_id", Integer, ForeignKey("right.id")),
)
class Parent(Base):
    __tablename__ = "left"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        secondary=association_table,
        back_populates="parents",
        cascade="all, delete",
    )
class Child(Base):
    __tablename__ = "right"
    id = mapped_column(Integer, primary_key=True)
    parents = relationship(
        "Parent",
        secondary=association_table,
        back_populates="children",
    )

上面,当使用 Session.delete() 标记要删除的 Parent 对象时,刷新过程将按照惯例从 association 表中删除相关行,但根据级联规则,它还将删除所有相关的 Child 行。

警告

如果上述 cascade="all, delete" 设置被配置在两个关系上,那么级联操作将继续通过所有 ParentChild 对象进行级联,加载遇到的每个 childrenparents 集合,并删除所有连接的内容。通常不希望双向配置“delete”级联。

另请参阅

从多对多表中删除行

使用外键 ON DELETE 处理多对多关系

使用 ORM 关系中的外键 ON DELETE 级联

SQLAlchemy 的“delete”级联的行为与数据库FOREIGN KEY约束的ON DELETE特性重叠。SQLAlchemy 允许使用 ForeignKeyForeignKeyConstraint 构造配置这些模式级别的 DDL 行为;在与 Table 元数据结合使用这些对象的用法在 ON UPDATE and ON DELETE 中有描述。

要在 relationship() 中使用ON DELETE外键级联,首先要注意的是 relationship.cascade 设置必须仍然配置为匹配所需的“删除”或“设置为 null”行为(使用delete级联或将其省略),以便 ORM 或数据库级别的约束将处理实际修改数据库中的数据的任务时,ORM 仍将能够适当地跟踪可能受到影响的本地存在的对象的状态。

然后在relationship() 上有一个额外的选项,指示 ORM 应该尝试在相关行上自行运行 DELETE/UPDATE 操作的程度,而不是依靠期望数据库端 FOREIGN KEY 约束级联处理该任务;这是 relationship.passive_deletes 参数,它接受选项 False(默认值)、True"all"

最典型的例子是,在删除父行时要删除子行,并且在相关的FOREIGN KEY约束上配置了ON DELETE CASCADE

class Parent(Base):
    __tablename__ = "parent"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        back_populates="parent",
        cascade="all, delete",
        passive_deletes=True,
    )
class Child(Base):
    __tablename__ = "child"
    id = mapped_column(Integer, primary_key=True)
    parent_id = mapped_column(Integer, ForeignKey("parent.id", ondelete="CASCADE"))
    parent = relationship("Parent", back_populates="children")

当删除父行时,上述配置的行为如下:

  1. 应用程序调用session.delete(my_parent),其中my_parentParent的实例。
  2. Session 下次将更改刷新到数据库时,my_parent.children 集合中的所有当前加载的项目都将被 ORM 删除,这意味着为每个记录发出了一个DELETE语句。
  3. 如果my_parent.children集合未加载,则不会发出DELETE语句。 如果在此relationship()上未设置relationship.passive_deletes标志,那么将会发出一个针对未加载的Child对象的SELECT语句。
  4. 针对my_parent行本身发出了一个DELETE语句。
  5. 数据库级别的ON DELETE CASCADE设置确保了所有引用受影响的parent行的child中的行也被删除。
  6. my_parent引用的Parent实例以及所有与此对象相关联且已加载Child实例(即发生了步骤 2)都将从Session中解除关联。

注意

要使用“ON DELETE CASCADE”,底层数据库引擎必须支持FOREIGN KEY约束,并且它们必须是强制性的:

  • 使用 MySQL 时,必须选择适当的存储引擎。 有关详细信息,请参阅 CREATE TABLE arguments including Storage Engines。
  • 使用 SQLite 时,必须显式启用外键支持。 有关详细信息,请参阅 Foreign Key Support。

在多对多关系中使用外键 ON DELETE

如使用 delete cascade 与多对多关系所述,“delete”级联也适用于多对多关系。 要利用ON DELETE CASCADE外键与多对多关系,必须在关联表上配置FOREIGN KEY指令。 这些指令可以处理自动从关联表中删除,但无法适应相关对象本身的自动删除。

在这种情况下,relationship.passive_deletes指令可以在删除操作期间为我们节省一些额外的SELECT语句,但仍然有一些集合是 ORM 将继续加载的,以定位受影响的子对象并正确处理它们。

注意

对此的假设优化可以包括一次针对关联表的所有父关联行的单个DELETE语句,然后使用RETURNING来定位受影响的相关子行,但这目前不是 ORM 工作单元实现的一部分。

在此配置中,我们在关联表的两个外键约束上都配置了ON DELETE CASCADE。我们在关系的父->子方向上配置了cascade="all, delete",然后我们可以在双向关系的另一侧上配置passive_deletes=True,如下所示:

association_table = Table(
    "association",
    Base.metadata,
    Column("left_id", Integer, ForeignKey("left.id", ondelete="CASCADE")),
    Column("right_id", Integer, ForeignKey("right.id", ondelete="CASCADE")),
)
class Parent(Base):
    __tablename__ = "left"
    id = mapped_column(Integer, primary_key=True)
    children = relationship(
        "Child",
        secondary=association_table,
        back_populates="parents",
        cascade="all, delete",
    )
class Child(Base):
    __tablename__ = "right"
    id = mapped_column(Integer, primary_key=True)
    parents = relationship(
        "Parent",
        secondary=association_table,
        back_populates="children",
        passive_deletes=True,
    )

使用上述配置,删除 Parent 对象的过程如下:

  1. 使用 Session.delete() 标记要删除的 Parent 对象。
  2. 当发生刷新时,如果未加载 Parent.children 集合,则 ORM 首先会发出 SELECT 语句,以加载与 Parent.children 对应的 Child 对象。
  3. 然后会为与该父行对应的 association 中的行发出 DELETE 语句。
  4. 对于受此即时删除影响的每个 Child 对象,因为配置了 passive_deletes=True,工作单元不需要尝试为每个 Child.parents 集合发出 SELECT 语句,因为假设将删除 association 中的相应行。
  5. 然后会为从 Parent.children 中加载的每个 Child 对象发出 DELETE 语句。

删除孤立

delete-orphan 级联为 delete 级联增加了行为,使得当子对象与父对象取消关联时,子对象将被标记为删除,而不仅仅是当父对象被标记为删除时。当处理由其父对象“拥有”的相关对象时,这是一个常见功能,具有非空的外键,以便从父集合中移除项目会导致其删除。

delete-orphan 级联意味着每个子对象一次只能有一个父对象,并且在绝大多数情况下仅配置在一对多关系上。在很少见的情况下,在多对一或多对多关系上设置它,“多”方可以通过配置 relationship.single_parent 参数,强制允许一次只有一个对象与父对象关联,从而在 Python 端建立验证,确保对象一次只与一个父对象关联,但这严重限制了“多”关系的功能,通常不是所期望的。

另请参阅

对于关系,delete-orphan 级联通常仅配置在一对多关系的“一”方,并且不配置在多对一或多对多关系的“多”方。 - 关于涉及 delete-orphan 级联的常见错误场景的背景信息。

合并

merge 级联表示 Session.merge() 操作应从 Session.merge() 调用的主体父对象传播到引用对象。此级联默认也是打开的。

刷新-过期

refresh-expire是一个不常见的选项,表示Session.expire()操作应该从父对象传播到引用的对象。当使用Session.refresh()时,引用的对象只是过期了,而不是实际刷新了。

清除

清除级联指的是当父对象从Session中使用Session.expunge()移除时,该操作应传播到引用的对象。

删除注意事项 - 删除集合和标量关系中引用的对象

通常,ORM 在刷新过程中永远不会修改集合或标量关系的内容。这意味着,如果你的类有一个指向对象集合的relationship(),或者一个指向单个对象的引用,比如多对一,当刷新过程发生时,这个属性的内容不会被修改。相反,预计Session最终会过期,通过Session.commit()的提交时过期行为或通过Session.expire()的显式使用。在那时,与该Session相关联的任何引用对象或集合都将被清除,并在下次访问时重新加载自身。

关于这种行为产生的常见困惑涉及到Session.delete()方法的使用。当在一个对象上调用Session.delete()并且Session被刷新时,该行将从数据库中删除。通过外键引用目标行的行,假设它们是使用两个映射对象类型之间的relationship()进行跟踪的,也会看到它们的外键属性被更新为 null,或者如果设置了删除级联,相关行也将被删除。然而,即使与已删除对象相关的行可能也被修改,在刷新范围内操作的对象上的关系绑定集合或对象引用不会发生任何更改。这意味着如果对象是相关集合的成员,它将仍然存在于 Python 端,直到该集合过期为止。同样,如果对象通过另一个对象的多对一或一对一引用,则该引用也将保留在该对象上,直到该对象也过期为止。

下面,我们说明了在将Address对象标记为删除后,即使在刷新后,它仍然存在于与父User关联的集合中:

>>> address = user.addresses[1]
>>> session.delete(address)
>>> session.flush()
>>> address in user.addresses
True

当以上会话提交时,所有属性都会过期。对user.addresses的下一次访问将重新加载集合,显示所需的状态:

>>> session.commit()
>>> address in user.addresses
False

有一个拦截Session.delete()并自动调用此过期的配方;参见ExpireRelationshipOnFKChange。然而,删除集合中的项目的通常做法是直接放弃使用Session.delete(),而是使用级联行为自动调用删除作为从父集合中删除对象的结果。delete-orphan级联实现了这一点,如下例所示:

class User(Base):
    __tablename__ = "user"
    # ...
    addresses = relationship("Address", cascade="all, delete-orphan")
# ...
del user.addresses[1]
session.flush()

在上面的例子中,当从User.addresses集合中移除Address对象时,delete-orphan级联的效果与将其传递给Session.delete()相同,都将Address对象标记为删除。

delete-orphan级联也可以应用于多对一或一对一关系,这样当一个对象与其父对象解除关联时,它也会被自动标记为删除。在多对一或一对一关系上使用delete-orphan级联需要一个额外的标志relationship.single_parent,它调用一个断言,指出这个相关对象不会同时与任何其他父对象共享:

class User(Base):
    # ...
    preference = relationship(
        "Preference", cascade="all, delete-orphan", single_parent=True
    )

上面,如果一个假设的Preference对象从一个User中移除,它将在刷新时被删除:

some_user.preference = None
session.flush()  # will delete the Preference object

另请参阅

级联以获取级联的详细信息。

事务和连接管理

原文:docs.sqlalchemy.org/en/20/orm/session_transaction.html

事务管理

在 1.4 版本中更改:会话事务管理已经修订为更清晰、更易于使用。特别是,它现在具有“自动开始”操作,这意味着可以控制事务开始的时间点,而不使用传统的“自动提交”模式。

Session一次跟踪单个“虚拟”事务的状态,使用一个称为SessionTransaction的对象。然后,此对象利用绑定到Session对象的基础Engine或引擎,根据需要使用Connection对象开始真实的连接级事务。

此“虚拟”事务在需要时会自动创建,或者可以使用Session.begin()方法启动。尽可能地支持 Python 上下文管理器的使用,既在创建Session对象的层面,也在维护SessionTransaction的范围方面。

假设我们从Session开始:

from sqlalchemy.orm import Session
session = Session(engine)

我们现在可以使用上下文管理器在一个确定的事务中运行操作:

with session.begin():
    session.add(some_object())
    session.add(some_other_object())
# commits transaction at the end, or rolls back if there
# was an exception raised

在上下文结束时,假设没有引发任何异常,则任何待处理的对象都将被刷新到数据库,并且数据库事务将被提交。如果在上述块内引发了异常,则事务将被回滚。在这两种情况下,上述Session在退出块后都已准备好用于后续的事务。

Session.begin()方法是可选的,Session也可以使用按需自动开始事务的“随时提交”方法;这些只需要提交或回滚:

session = Session(engine)
session.add(some_object())
session.add(some_other_object())
session.commit()  # commits
# will automatically begin again
result = session.execute(text("< some select statement >"))
session.add_all([more_objects, ...])
session.commit()  # commits
session.add(still_another_object)
session.flush()  # flush still_another_object
session.rollback()  # rolls back still_another_object

Session 本身具有一个 Session.close() 方法。如果 Session 是在尚未提交或回滚的事务内开始的,该方法将取消(即回滚)该事务,并清除 Session 对象状态中包含的所有对象。如果使用 Session 的方式不能保证调用 Session.commit()Session.rollback()(例如,不在上下文管理器或类似结构中),则可以使用 close 方法来确保释放所有资源:

# expunges all objects, releases all transactions unconditionally
# (with rollback), releases all database connections back to their
# engines
session.close()

最后,会话的构建/关闭过程本身也可以通过上下文管理器运行。这是确保 Session 对象使用范围在一个固定块内的最佳方式。首先通过 Session 构造函数进行说明:

with Session(engine) as session:
    session.add(some_object())
    session.add(some_other_object())
    session.commit()  # commits
    session.add(still_another_object)
    session.flush()  # flush still_another_object
    session.commit()  # commits
    result = session.execute(text("<some SELECT statement>"))
# remaining transactional state from the .execute() call is
# discarded

同样,sessionmaker 可以以相同的方式使用:

Session = sessionmaker(engine)
with Session() as session:
    with session.begin():
        session.add(some_object)
    # commits
# closes the Session

sessionmaker 本身包含一个 sessionmaker.begin() 方法,允许同时进行两个操作:

with Session.begin() as session:
    session.add(some_object)


SqlAlchemy 2.0 中文文档(二十三)(3)https://developer.aliyun.com/article/1560520

相关文章
|
3月前
|
SQL 数据库 Python
SqlAlchemy 2.0 中文文档(二十六)(4)
SqlAlchemy 2.0 中文文档(二十六)
43 2
|
3月前
|
SQL 关系型数据库 API
SqlAlchemy 2.0 中文文档(十七)(4)
SqlAlchemy 2.0 中文文档(十七)
53 4
|
3月前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(3)
SqlAlchemy 2.0 中文文档(二十二)
19 5
|
3月前
|
数据库 Python 容器
SqlAlchemy 2.0 中文文档(十四)(4)
SqlAlchemy 2.0 中文文档(十四)
26 1
|
3月前
|
API 数据库 C++
SqlAlchemy 2.0 中文文档(十四)(1)
SqlAlchemy 2.0 中文文档(十四)
22 1
|
3月前
|
API 数据库 Python
SqlAlchemy 2.0 中文文档(十四)(2)
SqlAlchemy 2.0 中文文档(十四)
28 1
|
3月前
|
API 数据库 Python
SqlAlchemy 2.0 中文文档(十四)(5)
SqlAlchemy 2.0 中文文档(十四)
35 1
|
3月前
|
数据库 Python 容器
SqlAlchemy 2.0 中文文档(十四)(3)
SqlAlchemy 2.0 中文文档(十四)
25 1
|
3月前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(2)
SqlAlchemy 2.0 中文文档(二十二)
32 3
|
3月前
|
SQL 存储 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(1)
SqlAlchemy 2.0 中文文档(二十二)
36 2