SqlAlchemy 2.0 中文文档(七十八)(1)

简介: SqlAlchemy 2.0 中文文档(七十八)


原文:docs.sqlalchemy.org/en/20/contents.html

SQLAlchemy 0.8 中的新功能是什么?

原文:docs.sqlalchemy.org/en/20/changelog/migration_08.html

关于本文档

本文档描述了 SQLAlchemy 版本 0.7(截至 2012 年 10 月正在进行维护发布)和 SQLAlchemy 版本 0.8(预计于 2013 年初发布)之间的更改。

文档日期:2012 年 10 月 25 日 更新日期:2013 年 3 月 9 日

介绍

本指南介绍了 SQLAlchemy 版本 0.8 中的新功能,并记录了影响用户将应用程序从 SQLAlchemy 0.7 系列迁移到 0.8 的更改。

SQLAlchemy 发布即将接近 1.0,自 0.5 以来的每个新版本都减少了主要的使用更改。大多数已经适应现代 0.7 模式的应用程序应该可以无需更改地迁移到 0.8。使用 0.6 甚至 0.5 模式的应用程序也应该可以直接迁移到 0.8,尽管较大的应用程序可能希望在每个中间版本中进行测试。

平台支持

现在面向 Python 2.5 及更高版本

SQLAlchemy 0.8 将面向 Python 2.5 及更高版本;对 Python 2.4 的兼容性将被删除。

内部将能够使用 Python 三元表达式(即,x if y else z),这将改善与使用y and x or z相比的情况,后者自然会导致一些错误,以及上下文管理器(即,with:)和在某些情况下可能会有助于代码可读性的try:/except:/else:块。

SQLAlchemy 最终将放弃对 2.5 的支持 - 当达到 2.6 作为基线时,SQLAlchemy 将转而使用 2.6/3.3 的就地兼容性,删除2to3工具的使用,并保持一个同时与 Python 2 和 3 兼容的源代码库。

新的 ORM 功能

重写的relationship()机制

0.8 版本在relationship()如何确定两个实体之间如何连接方面具有更加改进和强大的系统。新系统包括以下功能:

  • 在构建针对具有多个外键路径指向目标的类的relationship()时,不再需要primaryjoin参数。只需要foreign_keys参数来指定应包括的列:
class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    child_id_one = Column(Integer, ForeignKey("child.id"))
    child_id_two = Column(Integer, ForeignKey("child.id"))
    child_one = relationship("Child", foreign_keys=child_id_one)
    child_two = relationship("Child", foreign_keys=child_id_two)
class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
  • 支持自引用、复合外键的关系,其中一列指向自身。典型案例如下:
class Folder(Base):
    __tablename__ = "folder"
    __table_args__ = (
        ForeignKeyConstraint(
            ["account_id", "parent_id"], ["folder.account_id", "folder.folder_id"]
        ),
    )
    account_id = Column(Integer, primary_key=True)
    folder_id = Column(Integer, primary_key=True)
    parent_id = Column(Integer)
    name = Column(String)
    parent_folder = relationship(
        "Folder", backref="child_folders", remote_side=[account_id, folder_id]
    )
  • 上面,Folder指的是从account_id到其自身的父Folder的连接,并且parent_idfolder_id的连接。当 SQLAlchemy 构造自动连接时,不能再假设“远程”侧的所有列都被别名化,而“本地”侧的所有列都没有被别名化 - account_id在两侧都存在。因此,内部关系机制完全重写,以支持一个完全不同的系统,其中生成了两个副本的account_id,每个副本包含不同的注释以确定它们在语句中的作用。注意基本急加载中的连接条件:
SELECT
  folder.account_id  AS  folder_account_id,
  folder.folder_id  AS  folder_folder_id,
  folder.parent_id  AS  folder_parent_id,
  folder.name  AS  folder_name,
  folder_1.account_id  AS  folder_1_account_id,
  folder_1.folder_id  AS  folder_1_folder_id,
  folder_1.parent_id  AS  folder_1_parent_id,
  folder_1.name  AS  folder_1_name
FROM  folder
  LEFT  OUTER  JOIN  folder  AS  folder_1
  ON
  folder_1.account_id  =  folder.account_id
  AND  folder.folder_id  =  folder_1.parent_id
WHERE  folder.folder_id  =  ?  AND  folder.account_id  =  ?
  • 以前的复杂自定义连接条件,比如涉及函数和/或类型转换(CASTing)的条件,现在在大多数情况下都将按预期运行:
class HostEntry(Base):
    __tablename__ = "host_entry"
    id = Column(Integer, primary_key=True)
    ip_address = Column(INET)
    content = Column(String(50))
    # relationship() using explicit foreign_keys, remote_side
    parent_host = relationship(
        "HostEntry",
        primaryjoin=ip_address == cast(content, INET),
        foreign_keys=content,
        remote_side=ip_address,
    )
  • 新的relationship()机制利用了 SQLAlchemy 的一个概念,称为注释。这些注释也可以通过foreign()remote()函数明确提供给应用代码,无论是为了提高高级配置的可读性,还是直接注入一个精确的配置,绕过通常的连接检查启发式方法:
from sqlalchemy.orm import foreign, remote
class HostEntry(Base):
    __tablename__ = "host_entry"
    id = Column(Integer, primary_key=True)
    ip_address = Column(INET)
    content = Column(String(50))
    # relationship() using explicit foreign() and remote() annotations
    # in lieu of separate arguments
    parent_host = relationship(
        "HostEntry",
        primaryjoin=remote(ip_address) == cast(foreign(content), INET),
    )

另请参阅

配置关系连接方式 - 一个重新修订的关于relationship()的部分,详细说明了定制相关属性和集合访问的最新技术。

#1401 #610 ### 新的类/对象检查系统

许多 SQLAlchemy 用户正在编写需要检查映射类属性的系统,包括能够获取主键列、对象关系、普通属性等等,通常是为了构建数据编组系统,比如 JSON/XML 转换方案以及各种表单库。

最初,TableColumn模型是最初的检查点,具有完整的文档系统。虽然 SQLAlchemy ORM 模型也是完全可自省的,但这从来都不是一个完全稳定和受支持的功能,用户往往不清楚如何获取这些信息。

0.8 现在为此提供了一致、稳定且完全文档化的 API,包括一个检查系统,该系统适用于映射类、实例、属性以及其他核心和 ORM 构造。该系统的入口点是核心级的 inspect() 函数。在大多数情况下,被检查的对象已经是 SQLAlchemy 系统的一部分,例如 MapperInstanceStateInspector。在某些情况下,已经添加了新对象,其工作是在某些上下文中提供检查 API,例如 AliasedInspAttributeState

以下是一些关键功能的介绍:

>>> class User(Base):
...     __tablename__ = "user"
...     id = Column(Integer, primary_key=True)
...     name = Column(String)
...     name_syn = synonym(name)
...     addresses = relationship("Address")
>>> # universal entry point is inspect()
>>> b = inspect(User)
>>> # b in this case is the Mapper
>>> b
<Mapper at 0x101521950; User>
>>> # Column namespace
>>> b.columns.id
Column('id', Integer(), table=<user>, primary_key=True, nullable=False)
>>> # mapper's perspective of the primary key
>>> b.primary_key
(Column('id', Integer(), table=<user>, primary_key=True, nullable=False),)
>>> # MapperProperties available from .attrs
>>> b.attrs.keys()
['name_syn', 'addresses', 'id', 'name']
>>> # .column_attrs, .relationships, etc. filter this collection
>>> b.column_attrs.keys()
['id', 'name']
>>> list(b.relationships)
[<sqlalchemy.orm.properties.RelationshipProperty object at 0x1015212d0>]
>>> # they are also namespaces
>>> b.column_attrs.id
<sqlalchemy.orm.properties.ColumnProperty object at 0x101525090>
>>> b.relationships.addresses
<sqlalchemy.orm.properties.RelationshipProperty object at 0x1015212d0>
>>> # point inspect() at a mapped, class level attribute,
>>> # returns the attribute itself
>>> b = inspect(User.addresses)
>>> b
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x101521fd0>
>>> # From here we can get the mapper:
>>> b.mapper
<Mapper at 0x101525810; Address>
>>> # the parent inspector, in this case a mapper
>>> b.parent
<Mapper at 0x101521950; User>
>>> # an expression
>>> print(b.expression)
"user".id  =  address.user_id
>>> # inspect works on instances
>>> u1 = User(id=3, name="x")
>>> b = inspect(u1)
>>> # it returns the InstanceState
>>> b
<sqlalchemy.orm.state.InstanceState object at 0x10152bed0>
>>> # similar attrs accessor refers to the
>>> b.attrs.keys()
['id', 'name_syn', 'addresses', 'name']
>>> # attribute interface - from attrs, you get a state object
>>> b.attrs.id
<sqlalchemy.orm.state.AttributeState object at 0x10152bf90>
>>> # this object can give you, current value...
>>> b.attrs.id.value
3
>>> # ... current history
>>> b.attrs.id.history
History(added=[3], unchanged=(), deleted=())
>>> # InstanceState can also provide session state information
>>> # lets assume the object is persistent
>>> s = Session()
>>> s.add(u1)
>>> s.commit()
>>> # now we can get primary key identity, always
>>> # works in query.get()
>>> b.identity
(3,)
>>> # the mapper level key
>>> b.identity_key
(<class '__main__.User'>, (3,))
>>> # state within the session
>>> b.persistent, b.transient, b.deleted, b.detached
(True, False, False, False)
>>> # owning session
>>> b.session
<sqlalchemy.orm.session.Session object at 0x101701150>

另请参见

运行时检查 API

#2208

新的 with_polymorphic() 功能,可以在任何地方使用

Query.with_polymorphic() 方法允许用户指定在针对联接表实体进行查询时应该存在哪些表。不幸的是,该方法很笨拙,只适用于列表中的第一个实体,而且在使用和内部方面都有一些尴尬的行为。现在已经添加了一个新的增强功能到 aliased() 构造中,称为 with_polymorphic(),它允许任何实体被“别名”为其自身的“多态”版本,可以自由地在任何地方使用:

from sqlalchemy.orm import with_polymorphic
palias = with_polymorphic(Person, [Engineer, Manager])
session.query(Company).join(palias, Company.employees).filter(
    or_(Engineer.language == "java", Manager.hair == "pointy")
)

另请参见

使用 with_polymorphic() - 用于多态加载控制的最新更新文档。

#2333

of_type() 与 alias()、with_polymorphic()、any()、has()、joinedload()、subqueryload()、contains_eager() 配合使用

PropComparator.of_type() 方法用于在构建 SQL 表达式时指定要使用的特定子类型,该子类型作为其目标具有 多态 映射的 relationship() 的目标。现在可以使用该方法来针对 任意数量 的目标子类型,通过与新的 with_polymorphic() 函数结合使用:

# use eager loading in conjunction with with_polymorphic targets
Job_P = with_polymorphic(Job, [SubJob, ExtraJob], aliased=True)
q = (
    s.query(DataContainer)
    .join(DataContainer.jobs.of_type(Job_P))
    .options(contains_eager(DataContainer.jobs.of_type(Job_P)))
)

该方法现在在大多数接受常规关系属性的地方同样有效,包括与joinedload()subqueryload()contains_eager()等加载器函数以及与PropComparator.any()PropComparator.has()等比较方法一起:

# use eager loading in conjunction with with_polymorphic targets
Job_P = with_polymorphic(Job, [SubJob, ExtraJob], aliased=True)
q = (
    s.query(DataContainer)
    .join(DataContainer.jobs.of_type(Job_P))
    .options(contains_eager(DataContainer.jobs.of_type(Job_P)))
)
# pass subclasses to eager loads (implicitly applies with_polymorphic)
q = s.query(ParentThing).options(
    joinedload_all(ParentThing.container, DataContainer.jobs.of_type(SubJob))
)
# control self-referential aliasing with any()/has()
Job_A = aliased(Job)
q = (
    s.query(Job)
    .join(DataContainer.jobs)
    .filter(
        DataContainer.jobs.of_type(Job_A).any(
            and_(Job_A.id < Job.id, Job_A.type == "fred")
        )
    )
)

另请参见

加入特定子类型或 with_polymorphic()实体

#2438 #1106

事件可应用于未映射的超类

现在可以将映射器和实例事件与未映射的超类关联,这些事件将随着子类映射而传播。应使用propagate=True标志。此功能允许将事件与声明性基类关联:

from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
@event.listens_for("load", Base, propagate=True)
def on_load(target, context):
    print("New instance loaded:", target)
# on_load() will be applied to SomeClass
class SomeClass(Base):
    __tablename__ = "sometable"
    # ...

#2585

Declarative 区分模块/包

Declarative 的一个关键特性是能够使用它们的字符串名称引用其他映射类。类名注册表现在对给定类的拥有模块和包敏感。这些类可以在表达式中通过点名引用:

class Snack(Base):
    # ...
    peanuts = relationship(
        "nuts.Peanut", primaryjoin="nuts.Peanut.snack_id == Snack.id"
    )

该解析允许使用任何完整或部分消歧义的包名称。如果到特定类的路径仍然模糊,将引发错误。

#2338

Declarative 中的新 DeferredReflection 功能

“延迟反射”示例已移至 Declarative 中的受支持功能。此功能允许仅使用占位符Table元数据构建声明性映射类,直到调用prepare()步骤,给定一个Engine,以完全反射所有表并建立实际映射。该系统支持列的覆盖、单一和联接继承,以及每个引擎的不同基础。现在可以在一个步骤中针对现有表创建完整的声明性配置:

class ReflectedOne(DeferredReflection, Base):
    __abstract__ = True
class ReflectedTwo(DeferredReflection, Base):
    __abstract__ = True
class MyClass(ReflectedOne):
    __tablename__ = "mytable"
class MyOtherClass(ReflectedOne):
    __tablename__ = "myothertable"
class YetAnotherClass(ReflectedTwo):
    __tablename__ = "yetanothertable"
ReflectedOne.prepare(engine_one)
ReflectedTwo.prepare(engine_two)

另请参见

DeferredReflection

#2485

ORM 类现在被核心构造所接受

虽然与Query.filter()一起使用的 SQL 表达式,如User.id == 5,一直与核心构造兼容,例如select(),但当传递给select()Select.select_from()Select.correlate()时,映射类本身将不被识别。一个新的 SQL 注册系统允许一个映射类作为核心中的 FROM 子句被接受:

from sqlalchemy import select
stmt = select([User]).where(User.id == 5)

上面,映射的User类将扩展为TableUser被映射到其中的表。

#2245

Query.update()支持 UPDATE…FROM

新的 UPDATE…FROM 机制适用于 query.update()。下面,我们对SomeEntity执行 UPDATE 操作,添加一个 FROM 子句(或等效的,取决于后端)对SomeOtherEntity

query(SomeEntity).filter(SomeEntity.id == SomeOtherEntity.id).filter(
    SomeOtherEntity.foo == "bar"
).update({"data": "x"})

特别地,支持对连接继承实体的更新,前提是 UPDATE 的目标是本地表上的表,或者如果父表和子表混合,它们在查询中被显式连接。下面,假设EngineerPerson的一个连接子类:

query(Engineer).filter(Person.id == Engineer.id).filter(
    Person.name == "dilbert"
).update({"engineer_data": "java"})

将产生:

UPDATE  engineer  SET  engineer_data='java'  FROM  person
WHERE  person.id=engineer.id  AND  person.name='dilbert'

#2365

rollback()仅会回滚从 begin_nested()开始的“脏”对象

一项行为变更应该提高那些通过Session.begin_nested()使用 SAVEPOINT 的用户的效率 - 在rollback()时,只有自上次刷新以来被标记为脏的对象将被过期,其余的Session保持不变。这是因为对 SAVEPOINT 的 ROLLBACK 不会终止包含事务的隔离,因此除了当前事务中未刷新的更改外,不需要过期。

#2452

缓存示例现在使用 dogpile.cache

缓存示例现在使用dogpile.cache。Dogpile.cache 是 Beaker 缓存部分的重写,具有更简单和更快的操作,以及支持分布式锁定。

请注意,Dogpile 示例以及之前的 Beaker 示例中使用的 SQLAlchemy API 略有变化,特别是需要如 Beaker 示例中所示的这种变化:

--- examples/beaker_caching/caching_query.py
+++ examples/beaker_caching/caching_query.py
@@ -222,7 +222,8 @@
         """
         if query._current_path:
-            mapper, key = query._current_path[-2:]
+            mapper, prop = query._current_path[-2:]
+            key = prop.key
             for cls in mapper.class_.__mro__:
                 if (cls, key) in self._relationship_options:

另请参见

Dogpile 缓存

#2589


SqlAlchemy 2.0 中文文档(七十八)(2)https://developer.aliyun.com/article/1560957

相关文章
|
存储 算法 C++
【C++STL基础入门】vector向量基础使用
【C++STL基础入门】vector向量基础使用
617 1
|
机器学习/深度学习 弹性计算 云计算
阿里云ECS实例规格
阿里云ECS实例规格
|
5月前
|
算法 自动驾驶 机器人
基于Astar的复杂栅格地图路线规划算法matlab仿真
本项目基于A*算法实现复杂栅格地图的路径规划,适用于机器人导航、自动驾驶及游戏开发等领域。通过离散化现实环境为栅格地图,每个栅格表示空间区域属性(如可通行性)。A*算法利用启发函数评估节点,高效搜索从起点到终点的近似最优路径。项目在MATLAB2022a中运行,核心程序包含路径回溯与地图绘制功能,支持障碍物建模和路径可视化。理论结合实践,该方法具有重要应用价值,并可通过技术优化进一步提升性能。
|
10月前
|
人工智能 弹性计算 运维
操作系统控制台,让运维更简单!
操作系统控制台初体验,运维智能666!
415 37
操作系统控制台,让运维更简单!
|
小程序 数据挖掘 UED
餐饮店小程序开发定制桌边二维码点餐系统
随着技术不断进步,各行各业都在使用新工具来提高效率和服务质量。餐饮业也不例外。餐饮点餐小程序系统是基于微信公众平台开发的在线点餐方式。顾客可以通过手机微信扫描餐桌上的二维码,进入餐厅的点餐小程序,选择菜品、数量和口味,直接完成点餐。点餐系统会自动保存并发送给厨房,避免了传统手工点餐容易出错的问题。
|
Linux Docker Windows
Docker配置https证书案例
本文介绍了如何为Docker的Harbor服务配置HTTPS证书,包括安装Docker和Harbor、修改配置文件以使用证书、生成自签名证书、配置证书以及验证配置的步骤。
1090 3
Docker配置https证书案例
|
机器学习/深度学习 缓存 编解码
AIGC 商业化道路探索 - Stable Diffusion 商业化应用(上)
Stable Diffusion 应用到商业领域的案例越来越多,商用场景下的技术架构应当如何构建?本文基于阿里云近期的一个 Stable Diffusion 商业案例,对大规模底模切换、大量 LoRA 调优的场景提出一个商业场景适用的技术架构,并已实现部署交付,稳定运行。
|
存储 网络协议 网络安全
开源、自建,网站与ssl证书管理
【10月更文挑战第1天】
650 3
|
存储 JSON JavaScript
Vue.js开发中基于localStorage与sessionStorage的本地存储利器:Vue-ls插件使用详解
Vue.js开发中基于localStorage与sessionStorage的本地存储利器:Vue-ls插件使用详解
531 0
|
SQL 测试技术 API
SqlAlchemy 2.0 中文文档(一)(1)
SqlAlchemy 2.0 中文文档(一)
526 1
SqlAlchemy 2.0 中文文档(一)(1)