SqlAlchemy 2.0 中文文档(七十七)(2)

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
云数据库 RDS MySQL,集群版 2核4GB 100GB
推荐场景:
搭建个人博客
简介: SqlAlchemy 2.0 中文文档(七十七)

SqlAlchemy 2.0 中文文档(七十七)(1)https://developer.aliyun.com/article/1561173


行为改进

应该产生没有兼容性问题的改进,除非在极为罕见和异常的假设情况下,但最好知道这些改进,以防出现意外问题。

许多 JOIN 和 LEFT OUTER JOIN 表达式将不再被包装在 (SELECT * FROM …) AS ANON_1 中

多年来,SQLAlchemy ORM 一直无法将 JOIN 嵌套在现有 JOIN 的右侧(通常是 LEFT OUTER JOIN,因为 INNER JOIN 总是可以被展平):

SELECT  a.*,  b.*,  c.*  FROM  a  LEFT  OUTER  JOIN  (b  JOIN  c  ON  b.id  =  c.id)  ON  a.id

这是因为 SQLite 直到版本 3.7.16 都无法解析上述格式的语句:

SQLite version 3.7.15.2 2013-01-09 11:53:05
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> create table a(id integer);
sqlite> create table b(id integer);
sqlite> create table c(id integer);
sqlite> select a.id, b.id, c.id from a left outer join (b join c on b.id=c.id) on b.id=a.id;
Error: no such column: b.id

右外连接当然是解决右侧括号化的另一种方法;这将变得非常复杂和视觉上不愉快,但幸运的是 SQLite 也不支持 RIGHT OUTER JOIN 😃:

sqlite>  select  a.id,  b.id,  c.id  from  b  join  c  on  b.id=c.id
  ...>  right  outer  join  a  on  b.id=a.id;
Error:  RIGHT  and  FULL  OUTER  JOINs  are  not  currently  supported

早在 2005 年,不清楚其他数据库是否有问题,但今天似乎很明显,除 SQLite 外,每个经过测试的数据库都支持它(Oracle  8,一个非常古老的数据库,根本不支持 JOIN 关键字,但 SQLAlchemy 一直对 Oracle  的语法有一个简单的重写方案)。更糟糕的是,SQLAlchemy 通常的解决方法是在像 PostgreSQL 和 MySQL 这样的平台上应用  SELECT 通常会降低性能:

SELECT  a.*,  anon_1.*  FROM  a  LEFT  OUTER  JOIN  (
  SELECT  b.id  AS  b_id,  c.id  AS  c_id
  FROM  b  JOIN  c  ON  b.id  =  c.id
  )  AS  anon_1  ON  a.id=anon_1.b_id

类似上面形式的 JOIN 在处理连接表继承结构时很常见;每当使用 Query.join() 从某个父类连接到一个连接表子类,或者类似地使用 joinedload(),SQLAlchemy 的 ORM 总是确保不会渲染嵌套的 JOIN,以免查询无法在 SQLite 上运行。尽管 Core 一直支持更紧凑形式的 JOIN,ORM 必须避免使用它。

当在 ON 子句中存在特殊条件时,通过多对多关系生成连接时会出现另一个问题。考虑以下急加载连接:

session.query(Order).outerjoin(Order.items)

假设从 OrderItem 的多对多实际上指的是一个子类,如 Subitem,上述情况的 SQL 如下所示:

SELECT  order.id,  order.name
FROM  order  LEFT  OUTER  JOIN  order_item  ON  order.id  =  order_item.order_id
LEFT  OUTER  JOIN  item  ON  order_item.item_id  =  item.id  AND  item.type  =  'subitem'

上面的查询有什么问题?基本上,它将加载许多 order / order_item 行,其中 item.type == 'subitem' 的条件不成立。

从 SQLAlchemy 0.9 开始,采取了一种全新的方法。ORM 不再担心将 JOIN 嵌套在封闭 JOIN 的右侧,现在它会尽可能地渲染这些 JOIN,同时仍然返回正确的结果。当 SQL 语句被传递进行编译时,方言编译器会根据目标后端进行 重写 JOIN,如果该后端已知不支持右嵌套 JOIN(目前只有 SQLite - 如果其他后端有此问题,请告诉我们!)。

因此,一个常规的 query(Parent).join(Subclass) 现在通常会产生一个更简单的表达式:

SELECT  parent.id  AS  parent_id
FROM  parent  JOIN  (
  base_table  JOIN  subclass_table
  ON  base_table.id  =  subclass_table.id)  ON  parent.id  =  base_table.parent_id

类似 query(Parent).options(joinedload(Parent.subclasses)) 的连接急加载将对各个表进行别名处理,而不是包装在 ANON_1 中:

SELECT  parent.*,  base_table_1.*,  subclass_table_1.*  FROM  parent
  LEFT  OUTER  JOIN  (
  base_table  AS  base_table_1  JOIN  subclass_table  AS  subclass_table_1
  ON  base_table_1.id  =  subclass_table_1.id)
  ON  parent.id  =  base_table_1.parent_id

多对多连接和急加载将右嵌套“secondary”和“right”表:

SELECT  order.id,  order.name
FROM  order  LEFT  OUTER  JOIN
(order_item  JOIN  item  ON  order_item.item_id  =  item.id  AND  item.type  =  'subitem')
ON  order_item.order_id  =  order.id

所有这些连接,当使用Select语句渲染时,该语句明确指定use_labels=True,这对 ORM 发出的所有查询都是真实的,都是“连接重写”的候选对象,这是将所有这些右嵌套连接重写为嵌套的 SELECT 语句的过程,同时保持Select使用的相同标签。因此,SQLite,即使在 2013 年,也不支持这种非常常见的 SQL 语法,也要自己承担额外的复杂性,以上查询被重写为:

-- sqlite only!
SELECT  parent.id  AS  parent_id
  FROM  parent  JOIN  (
  SELECT  base_table.id  AS  base_table_id,
  base_table.parent_id  AS  base_table_parent_id,
  subclass_table.id  AS  subclass_table_id
  FROM  base_table  JOIN  subclass_table  ON  base_table.id  =  subclass_table.id
  )  AS  anon_1  ON  parent.id  =  anon_1.base_table_parent_id
-- sqlite only!
SELECT  parent.id  AS  parent_id,  anon_1.subclass_table_1_id  AS  subclass_table_1_id,
  anon_1.base_table_1_id  AS  base_table_1_id,
  anon_1.base_table_1_parent_id  AS  base_table_1_parent_id
FROM  parent  LEFT  OUTER  JOIN  (
  SELECT  base_table_1.id  AS  base_table_1_id,
  base_table_1.parent_id  AS  base_table_1_parent_id,
  subclass_table_1.id  AS  subclass_table_1_id
  FROM  base_table  AS  base_table_1
  JOIN  subclass_table  AS  subclass_table_1  ON  base_table_1.id  =  subclass_table_1.id
)  AS  anon_1  ON  parent.id  =  anon_1.base_table_1_parent_id
-- sqlite only!
SELECT  "order".id  AS  order_id
FROM  "order"  LEFT  OUTER  JOIN  (
  SELECT  order_item_1.order_id  AS  order_item_1_order_id,
  order_item_1.item_id  AS  order_item_1_item_id,
  item.id  AS  item_id,  item.type  AS  item_type
FROM  order_item  AS  order_item_1
  JOIN  item  ON  item.id  =  order_item_1.item_id  AND  item.type  IN  (?)
)  AS  anon_1  ON  "order".id  =  anon_1.order_item_1_order_id

注意

从 SQLAlchemy 1.1 开始,此功能中存在的 SQLite 的解决方法将在检测到 SQLite 版本3.7.16或更高版本时自动禁用自身,因为 SQLite 已修复了对右嵌套连接的支持。

Join.alias()aliased()with_polymorphic()函数现在支持一个新参数flat=True,用于构建别名的连接表实体,而不嵌入到 SELECT 中。默认情况下,此标志未启用,以帮助向后兼容性 - 但现在可以将“多态”可选择地作为目标连接,而不生成任何子查询:

employee_alias = with_polymorphic(Person, [Engineer, Manager], flat=True)
session.query(Company).join(Company.employees.of_type(employee_alias)).filter(
    or_(Engineer.primary_language == "python", Manager.manager_name == "dilbert")
)

生成(除了 SQLite):

SELECT  companies.company_id  AS  companies_company_id,  companies.name  AS  companies_name
FROM  companies  JOIN  (
  people  AS  people_1
  LEFT  OUTER  JOIN  engineers  AS  engineers_1  ON  people_1.person_id  =  engineers_1.person_id
  LEFT  OUTER  JOIN  managers  AS  managers_1  ON  people_1.person_id  =  managers_1.person_id
)  ON  companies.company_id  =  people_1.company_id
WHERE  engineers.primary_language  =  %(primary_language_1)s
  OR  managers.manager_name  =  %(manager_name_1)s

#2369 #2587 ### 可在连接的急切加载中使用右嵌套内连接

从版本 0.9.4 开始,在连接的急切加载情况下,可以启用上述提到的右嵌套连接,其中“外部”连接与右侧的“内部”连接相关联。

通常,像下面这样的连接急切加载链:

query(User).options(
    joinedload("orders", innerjoin=False).joinedload("items", innerjoin=True)
)

不会产生内连接;由于从 user->order 的 LEFT OUTER JOIN,连接的急切加载无法使用从 order->items 到 INNER join,而不更改返回的用户行,并且会忽略“链接”innerjoin=True指令。0.9.0 应该交付的是,而不是:

FROM  users  LEFT  OUTER  JOIN  orders  ON  <onclause>  LEFT  OUTER  JOIN  items  ON  <onclause>

新的“右嵌套连接是可以的”逻辑将启动,我们将得到:

FROM  users  LEFT  OUTER  JOIN  (orders  JOIN  items  ON  <onclause>)  ON  <onclause>

由于我们错过了这一点,为了避免进一步的退化,我们通过向joinedload.innerjoin指定字符串"nested"来添加上述功能:

query(User).options(
    joinedload("orders", innerjoin=False).joinedload("items", innerjoin="nested")
)

此功能是 0.9.4 中的新功能。

#2976

ORM 可以使用 RETURNING 高效地获取刚生成的 INSERT/UPDATE 默认值

Mapper 长期以来一直支持一个名为 eager_defaults=True  的未记录的标志。此标志的作用是,当进行 INSERT 或 UPDATE 操作时,如果知道行具有由服务器生成的默认值,则会立即跟随一个  SELECT  来“急切地”加载这些新值。通常,服务器生成的列会在对象上标记为“过期”,因此除非应用程序在刷新后立即访问这些列,否则不会产生任何开销。因此,eager_defaults 标志并不是很有用,因为它只会降低性能,并且只存在于支持需要默认值在刷新过程中立即可用的奇特事件方案的情况下。

在 0.9 版本中,由于版本 ID 的增强,eager_defaults 现在可以为这些值发出 RETURNING 子句,因此在具有强大 RETURNING 支持的后端,特别是 PostgreSQL 上,ORM 可以在 INSERT 或 UPDATE 中内联获取新生成的默认和 SQL 表达式值。当启用 eager_defaults 时,将自动使用 RETURNING,当目标后端和 Table 支持“隐式返回”时。

对于某些查询,子查询预加载将在最内层的 SELECT 上应用 DISTINCT

在涉及到一对多关系时,子查询预加载可能会生成重复行的数量,因此当连接目标列不包含主键时,会对最内层的 SELECT 应用 DISTINCT 关键字,例如在沿着一对多加载时。

也就是说,在从 A->B 的一对多子查询加载时:

SELECT  b.id  AS  b_id,  b.name  AS  b_name,  anon_1.b_id  AS  a_b_id
FROM  (SELECT  DISTINCT  a_b_id  FROM  a)  AS  anon_1
JOIN  b  ON  b.id  =  anon_1.a_b_id

由于 a.b_id 是一个非唯一的外键,所以会应用 DISTINCT,以消除冗余的 a.b_id。此行为可以通过为特定的 relationship() 设置标志 distinct_target_key 来无条件地打开或关闭,将值设置为 True 表示无条件打开,False 表示无条件关闭,None 表示当目标 SELECT 针对不包含完整主键的列时生效。在 0.9 版本中,None 是默认值。

这个选项也被回溯到了 0.8 版本,其中 distinct_target_key 选项的默认值为 False

虽然此处的功能旨在通过消除重复行来帮助性能,但 SQL 中的 DISTINCT 关键字本身可能会对性能产生负面影响。如果 SELECT  中的列没有索引,则 DISTINCT 可能会对行集执行 ORDER  BY,这可能会很昂贵。通过将此功能限制在外键上,希望外键无论如何都已被索引,可以预期新的默认值是合理的。

该功能也不能消除每种可能的重复行情况;如果在连接链中的其他地方存在多对一关系,重复行可能仍然存在。

#2836 ### 反向引用处理程序现在可以传播多于一级的深度

属性事件传递其“发起者”的机制已经发生了变化;不再传递AttributeImpl,而是传递一个新的对象Event;该对象引用AttributeImpl以及一个“操作令牌”,表示操作是追加、移除还是替换操作。

属性事件系统不再查看这个“发起者”对象以阻止递归系列的属性事件。相反,防止由于相互依赖的反向引用处理程序而导致无限递归的系统已经移动到了  ORM 反向引用事件处理程序中,这些处理程序现在负责确保相互依赖事件链(例如向集合 A.bs 追加,响应中设置多对一属性  B.a)不会进入无限递归流。这里的理念是,反向引用系统,通过更详细和控制事件传播,最终可以允许超过一级深度的操作发生;典型情况是集合追加导致多对一替换操作,进而应导致项目从先前的集合中移除的情况:

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    children = relationship("Child", backref="parent")
class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
    parent_id = Column(ForeignKey("parent.id"))
p1 = Parent()
p2 = Parent()
c1 = Child()
p1.children.append(c1)
assert c1.parent is p1  # backref event establishes c1.parent as p1
p2.children.append(c1)
assert c1.parent is p2  # backref event establishes c1.parent as p2
assert c1 not in p1.children  # second backref event removes c1 from p1.children

在此更改之前,c1对象仍然会存在于p1.children中,即使它同时也存在于p2.children中;反向引用处理程序会停止在用p2替换c1.parent而不是p1的操作上。在 0.9 版本中,使用更详细的Event对象以及让反向引用处理程序对这些对象做出更详细的决策,传播可以继续进行,从而将c1p1.children中移除,同时保持检查以防止传播进入无限递归循环。

使用 AttributeEvents.set()AttributeEvents.append()AttributeEvents.remove() 事件的终端用户代码可能需要修改,以防止递归循环,因为在缺少反向引用事件处理程序的情况下,属性系统不再阻止事件链无限传播。此外,依赖于 initiator 值的代码将需要调整到新的 API,并且还必须准备好 initiator 值在一系列反向引用引发的事件中从其原始值更改,因为现在反向引用处理程序可能会为某些操作交换一个新的 initiator 值。

#2789 ### 类型系统现在处理呈现“字面绑定”值的任务

TypeEngineTypeDecorator 分别添加了新的方法 TypeEngine.literal_processor()TypeDecorator.process_literal_param(),它们负责呈现所谓的“内联字面参数” - 由于编译器配置的原因,这些参数通常呈现为“绑定”值,但实际上是被内联呈现到 SQL 语句中。此功能用于生成诸如 CheckConstraint 这样的结构的 DDL,以及当使用诸如 op.inline_literal() 这样的结构时,Alembic 会使用它。以前,一个简单的“isinstance”检查检查了一些基本类型,并且“绑定处理器”被无条件使用,导致字符串过早编码为 utf-8 的问题。

使用 TypeDecorator 编写的自定义类型应继续在“内联文字”场景中工作,因为 TypeDecorator.process_literal_param() 默认会回退到 TypeDecorator.process_bind_param(),因为这些方法通常处理的是数据操作,而不是数据如何呈现给数据库。TypeDecorator.process_literal_param() 可以被指定为明确产生一个表示如何将值渲染成内联 DDL 语句的字符串。

#2838 ### 模式标识符现在携带其自身的引号信息

此更改简化了 Core 对所谓的“引号”标志的使用,比如传递给 TableColumnquote 标志。该标志现在内部化在字符串名称本身中,现在表示为 quoted_name 的一个实例,即一个字符串子类。 IdentifierPreparer 现在仅依赖于由 quoted_name 对象报告的引号首选项,而不是在大多数情况下检查任何显式的 quote 标志。这里解决的问题包括,各种区分大小写的方法(例如 Engine.has_table() 以及方言内部的类似方法)现在能够以显式引号的名称正确地运行,而不需要复杂化或引入与引号标志的细节不兼容的更改到这些  API(其中许多是第三方的) - 特别是,更广泛范围的标识符现在能够与所谓的“大写”后端(像 Oracle、Firebird 和 DB2  这样的后端)正确地工作,这些后端使用全部大写存储和报告不区分大小写的名称的表和列名。

内部根据需要使用 quoted_name 对象;但是,如果其他关键字需要固定的引号首选项,则该类是公开可用的。

#2812 ### 改进布尔常量、NULL 常量、连接词的渲染

新功能已添加到 true()false() 常量中,特别是与 and_()or_() 函数以及 WHERE/HAVING 子句与这些类型、整体布尔类型和 null() 常量的行为结合使用时。

从这样的表开始:

from sqlalchemy import Table, Boolean, Integer, Column, MetaData
t1 = Table("t", MetaData(), Column("x", Boolean()), Column("y", Integer))

在不具有 true/false 常量行为的后端上,选择构造现在将布尔列渲染为二进制表达式:

>>> from sqlalchemy import select, and_, false, true
>>> from sqlalchemy.dialects import mysql, postgresql
>>> print(select([t1]).where(t1.c.x).compile(dialect=mysql.dialect()))
SELECT  t.x,  t.y  FROM  t  WHERE  t.x  =  1 

and_()or_() 构造现在将表现出准“短路”行为,即在存在 true()false() 常量时截断渲染表达式:

>>> print(
...     select([t1]).where(and_(t1.c.y > 5, false())).compile(dialect=postgresql.dialect())
... )
SELECT  t.x,  t.y  FROM  t  WHERE  false 

true() 可以用作构建表达式的基础:

>>> expr = true()
>>> expr = expr & (t1.c.y > 5)
>>> print(select([t1]).where(expr))
SELECT  t.x,  t.y  FROM  t  WHERE  t.y  >  :y_1 

布尔常量 true()false() 本身渲染为 0 = 11 = 1,对于没有布尔常量的后端:

>>> print(select([t1]).where(and_(t1.c.y > 5, false())).compile(dialect=mysql.dialect()))
SELECT  t.x,  t.y  FROM  t  WHERE  0  =  1 

None 的解释,虽然不是特别有效的 SQL,但至少现在是一致的:

>>> print(select([t1.c.x]).where(None))
SELECT  t.x  FROM  t  WHERE  NULL
>>> print(select([t1.c.x]).where(None).where(None))
SELECT  t.x  FROM  t  WHERE  NULL  AND  NULL
>>> print(select([t1.c.x]).where(and_(None, None)))
SELECT  t.x  FROM  t  WHERE  NULL  AND  NULL 

#2804 ### 标签构造现在可以��� ORDER BY 中仅呈现为其名称

在 SELECT 的列子句和 ORDER BY 子句中都使用 Label 的情况下,标签将仅在 ORDER BY 子句中呈现为其名称,假设底层方言报告支持此功能。

例如,像这样的示例:

from sqlalchemy.sql import table, column, select, func
t = table("t", column("c1"), column("c2"))
expr = (func.foo(t.c.c1) + t.c.c2).label("expr")
stmt = select([expr]).order_by(expr)
print(stmt)

在 0.9 之前将渲染为:

SELECT  foo(t.c1)  +  t.c2  AS  expr
FROM  t  ORDER  BY  foo(t.c1)  +  t.c2

现在将渲染为:

SELECT  foo(t.c1)  +  t.c2  AS  expr
FROM  t  ORDER  BY  expr

ORDER BY 仅在标签未在 ORDER BY 中进一步嵌入到表达式中时呈现标签,除了简单的 ASCDESC

上述格式在所有经过测试的数据库上都有效,但可能与旧数据库版本(MySQL 4?Oracle 8?等)存在兼容性问题。根据用户报告,我们可以添加规则,根据数据库版本检测禁用该功能。

#1068 ### RowProxy现在具有元组排序行为

RowProxy对象的行为很像一个元组,但直到现在,如果使用sorted()对它们的列表进行排序,它们不会像元组一样排序。现在,__eq__()方法将两侧都作为元组进行比较,并且还添加了一个__lt__()方法:

users.insert().execute(
    dict(user_id=1, user_name="foo"),
    dict(user_id=2, user_name="bar"),
    dict(user_id=3, user_name="def"),
)
rows = users.select().order_by(users.c.user_name).execute().fetchall()
eq_(rows, [(2, "bar"), (3, "def"), (1, "foo")])
eq_(sorted(rows), [(1, "foo"), (2, "bar"), (3, "def")])

#2848 ### 当bindparam()构造没有类型时,通过复制进行升级,当有类型可用时

“升级”bindparam()构造以采用封闭表达式类型的逻辑已经通过两种方式得到改进。首先,在分配新类型之前,bindparam()对象会被复制,以便给定的bindparam()不会就地突变。其次,在编译InsertUpdate构造时,通过ValuesBase.values()方法设置的“values”在语句中也会发生相同的操作。

如果给定一个无类型的bindparam():

bp = bindparam("some_col")

如果我们像下面这样使用这个参数:

expr = mytable.c.col == bp

bp的类型仍然是NullType,但是如果mytable.c.col的类型是String,那么二元表达式的右侧expr.right将采用String类型。以前,bp本身会被就地更改为具有String类型。

同样,这个操作也会在InsertUpdate中发生:

stmt = mytable.update().values(col=bp)

在上面的例子中,bp保持不变,但在执行语句时将使用String类型,我们可以通过检查binds字典来看到这一点:

>>> compiled = stmt.compile()
>>> compiled.binds["some_col"].type
String

该功能允许自定义类型在 INSERT/UPDATE 语句中产生预期效果,而无需在每个bindparam()表达式中显式指定这些类型。

可能向后兼容的更改涉及两种不太可能的情况。由于绑定参数是克隆的,用户不应该依赖于对一旦创建的bindparam()构造进行就地更改。此外,使用bindparam()在依赖于bindparam()未根据分配给的列进行类型化的事实的InsertUpdate语句的代码将不再以这种方式运行。

#2850 ### 列可以可靠地从通过外键引用的列中获取其类型

有一个长期存在的行为,即可以声明没有类型的Column,只要该ColumnForeignKeyConstraint引用,并且引用列的类型将被复制到此列中。问题在于这个功能从来没有很好地工作过,也没有得到维护。核心问题是ForeignKey对象不知道它引用的目标Column是什么,直到被询问,通常是第一次使用外键来构造Join时。因此,在那个时候,父Column将没有类型,或更具体地说,它将具有默认类型NullType

尽管花费了很长时间,重新组织ForeignKey对象初始化的工作已经完成,使得这个功能最终可以正常工作。这个改变的核心是ForeignKey.column属性不再延迟初始化目标Column的位置;这个系统的问题在于拥有的Column会一直被固定为NullType类型,直到ForeignKey被使用。

在新版本中,ForeignKey通过内部附加事件与最终引用的Column协调,因此一旦引用的ColumnMetaData关联,所有引用它的ForeignKey对象都会收到一条消息,告诉它们需要初始化其父列。这个系统更加复杂,但更加稳固;作为奖励,现在已经为各种Column / ForeignKey配置场景设置了测试,并且错误消息已经改进,对不少于七种不同的错误条件进行了非常具体的描述。

现在正确工作的场景包括:

  1. 当目标Column与相同的MetaData关联时,Column上的类型会立即出现;无论哪一边先配置都可以:
>>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKey
>>> metadata = MetaData()
>>> t2 = Table("t2", metadata, Column("t1id", ForeignKey("t1.id")))
>>> t2.c.t1id.type
NullType()
>>> t1 = Table("t1", metadata, Column("id", Integer, primary_key=True))
>>> t2.c.t1id.type
Integer()
  1. 系统现在也可以使用ForeignKeyConstraint
>>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKeyConstraint
>>> metadata = MetaData()
>>> t2 = Table(
...     "t2",
...     metadata,
...     Column("t1a"),
...     Column("t1b"),
...     ForeignKeyConstraint(["t1a", "t1b"], ["t1.a", "t1.b"]),
... )
>>> t2.c.t1a.type
NullType()
>>> t2.c.t1b.type
NullType()
>>> t1 = Table(
...     "t1",
...     metadata,
...     Column("a", Integer, primary_key=True),
...     Column("b", Integer, primary_key=True),
... )
>>> t2.c.t1a.type
Integer()
>>> t2.c.t1b.type
Integer()
  1. 它甚至适用于“多跳” - 即,一个指向另一个ColumnColumnForeignKey:
>>> from sqlalchemy import Table, MetaData, Column, Integer, ForeignKey
>>> metadata = MetaData()
>>> t2 = Table("t2", metadata, Column("t1id", ForeignKey("t1.id")))
>>> t3 = Table("t3", metadata, Column("t2t1id", ForeignKey("t2.t1id")))
>>> t2.c.t1id.type
NullType()
>>> t3.c.t2t1id.type
NullType()
>>> t1 = Table("t1", metadata, Column("id", Integer, primary_key=True))
>>> t2.c.t1id.type
Integer()
>>> t3.c.t2t1id.type
Integer()

#1765

方言更改

Firebird fdb 现在是默认的 Firebird 方言。

如果创建引擎时没有指定方言,则现在使用 fdb 方言,即 firebird://fdb 是一个与 kinterbasdb 兼容的 DBAPI,根据 Firebird 项目的说法,现在是他们官方的 Python 驱动程序。

#2504

Firebird fdbkinterbasdb 默认设置 retaining=False

fdbkinterbasdb DBAPI 都支持一个标志 retaining=True,可以传递给其连接的 commit()rollback() 方法。文档中对这个标志的理由是,DBAPI 可以重用内部事务状态进行后续事务,以提高性能。然而,更新的文档提到了 Firebird 的“垃圾回收”分析,表明这个标志可能对数据库处理清理任务的能力产生负面影响,并因此被报告为降低性能。

鉴于这些信息,目前不清楚这个标志实际上如何可用,而且由于它似乎只是一个性能增强功能,现在默认值为 False。可以通过向 create_engine() 调用传递标志 retaining=True 来控制该值。这是一个新标志,从 0.8.2 版本开始添加,因此在 0.8.2 版本的应用程序可以根据需要将其设置为 TrueFalse

另请参阅

sqlalchemy.dialects.firebird.fdb

sqlalchemy.dialects.firebird.kinterbasdb

pythonhosted.org/fdb/usage-guide.html#retaining-transactions - 有关“保留”标志的信息。

#2763

介绍

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

请仔细查看行为变化 - ORM 和行为变化 - Core,可能存在不兼容的变化。

平台支持

现在针对 Python 2.6 及更高版本,Python 3 不再需要 2to3

第一个 0.9 版本的成就是移除了对 Python 3 兼容性的 2to3 工具的依赖。为了使这更加直观,现在目标最低的 Python  发布版本是 2.6,它具有与 Python 3 广泛的交叉兼容性。所有 SQLAlchemy 模块和单元测试现在都能在任何从 2.6 开始的  Python 解释器上同样良好地解释,包括 3.1 和 3.2 解释器。

#2671

C 扩展在 Python 3 上受支持

C 扩展已被移植以支持 Python 3,并且现在在 Python 2 和 Python 3 环境中都构建。

#2161

现在目标是 Python 2.6 及更高版本,Python 3 不再需要 2to3

第一个 0.9 版本的成就是移除了对 Python 3 兼容性的 2to3 工具的依赖。为了使这更加直观,现在目标最低的 Python  发布版本是 2.6,它具有与 Python 3 广泛的交叉兼容性。所有 SQLAlchemy 模块和单元测试现在都能在任何从 2.6 开始的  Python 解释器上同样良好地解释,包括 3.1 和 3.2 解释器。

#2671

C 扩展在 Python 3 上受支持

C 扩展已被移植以支持 Python 3,并且现在在 Python 2 和 Python 3 环境中都构建。

#2161


SqlAlchemy 2.0 中文文档(七十七)(3)https://developer.aliyun.com/article/1561179

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
7天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(七十一)(4)
SqlAlchemy 2.0 中文文档(七十一)
12 1
|
7天前
|
Oracle 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十一)(1)
SqlAlchemy 2.0 中文文档(七十一)
9 1
|
10天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十九)(5)
SqlAlchemy 2.0 中文文档(七十九)
11 2
|
10天前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十六)(1)
SqlAlchemy 2.0 中文文档(七十六)
12 2
|
11天前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(八十一)(1)
SqlAlchemy 2.0 中文文档(八十一)
13 2
|
11天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(八十一)(5)
SqlAlchemy 2.0 中文文档(八十一)
16 1
|
11天前
|
SQL Oracle 关系型数据库
SqlAlchemy 2.0 中文文档(八十一)(2)
SqlAlchemy 2.0 中文文档(八十一)
18 1
|
11天前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(八十一)(3)
SqlAlchemy 2.0 中文文档(八十一)
14 1
|
11天前
|
存储 SQL Java
SqlAlchemy 2.0 中文文档(八十一)(4)
SqlAlchemy 2.0 中文文档(八十一)
16 1
|
10天前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(七十七)(1)
SqlAlchemy 2.0 中文文档(七十七)
15 0