非传统映射¶

根据多个表映射类

映射器可以针对任意关系单元(调用 可选择的 )除了普通的表格。例如, join() 函数创建一个由多个表组成的可选单元,并使用自己的复合主键完成,该主键可以与 Table ::

from sqlalchemy import Table, Column, Integer, \
        String, MetaData, join, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import column_property

metadata = MetaData()

# define two Table objects
user_table = Table('user', metadata,
            Column('id', Integer, primary_key=True),
            Column('name', String),
        )

address_table = Table('address', metadata,
            Column('id', Integer, primary_key=True),
            Column('user_id', Integer, ForeignKey('user.id')),
            Column('email_address', String)
            )

# define a join between them.  This
# takes place across the user.id and address.user_id
# columns.
user_address_join = join(user_table, address_table)

Base = declarative_base()

# map to it
class AddressUser(Base):
    __table__ = user_address_join

    id = column_property(user_table.c.id, address_table.c.user_id)
    address_id = address_table.c.id

在上面的示例中,join表示 user 以及 address 表。这个 user.idaddress.user_id 列与外键相等,因此在映射中它们被定义为一个属性, AddressUser.id 使用 column_property() 指示专用列映射。根据配置的这一部分,映射将从 user.id 进入 address.user_id 发生刷新时的列。

另外, address.id 列显式映射到名为 address_id . 这是为了 消除歧义 的映射 address.id 来自相同名称的列 AddressUser.id 属性,此处已分配用于引用 user 表与 address.user_id 外键。

上述映射的自然主键是 (user.id, address.id) ,因为这些是 useraddress 表格合并在一起。的标识 AddressUser 对象将以这两个值表示,并由 AddressUser 对象AS (AddressUser.id, AddressUser.address_id) .

根据任意选择映射类

类似于针对连接的映射,普通 select() 对象也可以与映射器一起使用。下面的示例片段说明如何映射一个名为 Customer 到A select() 其中包括到子查询的联接:

from sqlalchemy import select, func

subq = select([
            func.count(orders.c.id).label('order_count'),
            func.max(orders.c.price).label('highest_order'),
            orders.c.customer_id
            ]).group_by(orders.c.customer_id).alias()

customer_select = select([customers, subq]).\
            select_from(
                join(customers, subq,
                        customers.c.id == subq.c.customer_id)
            ).alias()

class Customer(Base):
    __table__ = customer_select

上面的整行由 customer_select 将是所有列的 customers 表,除了 subq 子查询,即 order_counthighest_ordercustomer_id . 映射 Customer 然后创建一个包含这些属性的类。

当ORM保持新的 Customer ,只有 customers 表将实际接收一个插入。这是因为 orders 映射中不表示表;ORM将只向其映射了主键的表中发出一个插入。

注解

几乎不需要映射到任意的select语句,尤其是如上所述的复杂语句;它必然倾向于生成比直接查询构造生成的查询效率低的复杂查询。这种做法在某种程度上是基于早期的SQLAlchemy历史,其中 mapper() 构造的目的是表示主查询接口;在现代用法中, Query 对象可以用来构造几乎所有的select语句,包括复杂的复合语句,并且应该优于“映射到可选”方法。

一个类的多个映射器

在现代的SQLAlchemy中,一个特定的类只被一个所谓的 初级的 一次映射。这个映射器涉及三个主要功能领域:查询、持久性和映射类的插入。主映射器的基本原理与以下事实有关: mapper() 修改类本身,不仅将其持久化到特定的 Table ,而且 instrumenting 类上的属性,这些属性是根据表元数据专门构造的。不可能有多个映射器以相同的度量与类关联,因为实际上只有一个映射器可以对类进行检测。

然而,有一类映射器称为 非初级 允许附加映射器与类关联,但使用范围有限的映射器。此范围通常适用于能够从备用表或可选单元加载行,但仍然生成使用主映射最终持久化的类。非主映射器是根据已经用主映射器映射的类使用经典的映射样式创建的,它涉及使用 non_primary 标志。

非主映射器在现代SQLAlchemy中的使用非常有限,因为现在可以使用 Query 直接对象。

对于非主映射器,实际上只有一个用例,即我们希望构建一个 relationship() 对于这样一个映射器,这在我们的关系正试图使用多个表和/或表之间的联接将两个类连接在一起的罕见和高级情况下非常有用。此模式的一个示例位于 relationship_non_primary_mapper .

就一个类的用例而言,它实际上可以在不同的场景下完全持久化到不同的表中,非常早的SQLAlchemy版本提供了一个从Hibernate改编的特性,称为“实体名”特性。然而,一旦映射的类本身成为SQL表达式构造的源,这种用例在sqlAlchemy中就变得不可行;也就是说,类的属性本身直接链接到映射的表列。该特性被删除,并替换为一种简单的面向方法的方法来完成这项任务,而不需要使用任何含糊不清的工具——创建新的子类,每个子类都单独映射。此模式现在可作为方法在 Entity Name .