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

相关文章
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(2)
SqlAlchemy 2.0 中文文档(七十六)
17 2
|
2天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(1)
SqlAlchemy 2.0 中文文档(七十六)
11 2
|
2天前
|
SQL 关系型数据库 PostgreSQL
SqlAlchemy 2.0 中文文档(七十八)(4)
SqlAlchemy 2.0 中文文档(七十八)
10 0
|
2天前
|
SQL 关系型数据库 测试技术
SqlAlchemy 2.0 中文文档(七十八)(5)
SqlAlchemy 2.0 中文文档(七十八)
8 0
|
2天前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(七十八)(2)
SqlAlchemy 2.0 中文文档(七十八)
10 0
|
2天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(七十八)(3)
SqlAlchemy 2.0 中文文档(七十八)
10 0
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(六十七)(2)
SqlAlchemy 2.0 中文文档(六十七)
7 0
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(六十七)(4)
SqlAlchemy 2.0 中文文档(六十七)
7 0
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(六十七)(1)
SqlAlchemy 2.0 中文文档(六十七)
7 0
|
3天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(六十七)(3)
SqlAlchemy 2.0 中文文档(六十七)
9 0