版本:1.1.0b2 |发布日期:2016年7月1日

SQLAlchemy 1.1文档

非传统映射

根据多个表映射一个类

除了普通表格以外,映射器可以针对任意的关系单元(称为selectables)构建。例如,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

在上面的例子中,连接表示useraddress表的列。user.idaddress.user_id列由外键等同,因此在映射中它们被定义为一个属性,AddressUser.id ,用column_property()来表示专门的列映射。根据这部分配置,当发生刷新时,映射会将user.id中的新主键值复制到address.user_id列中。

此外,address.id列显式映射到名为address_id的属性。This is to disambiguate the mapping of the address.id column from the same-named AddressUser.id attribute, which here has been assigned to refer to the user table combined with the address.user_id foreign key.

上面映射的自然主键是(user.id, address.id)的组合,因为它们是主键列将useraddress表合并在一起。The identity of an AddressUser object will be in terms of these two values, and is represented from an AddressUser object as (AddressUser.id, AddressUser.address_id).

根据任意选择映射类

与针对连接的映射类似,也可以将普通的select()对象与映射器一起使用。下面的示例片段说明了如何将一个名为Customer的类映射到一个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

Above, the full row represented by customer_select will be all the columns of the customers table, in addition to those columns exposed by the subq subquery, which are order_count, highest_order, and customer_id. Customer类映射到这个可选项然后创建一个包含这些属性的类。

当ORM持续Customer的新实例时,只有customers表将实际接收到一个INSERT。这是因为orders表的主键没有在映射中表示; ORM只会发出一个INSERT到一个已经映射了主键的表中。

注意

映射到任意SELECT语句,尤其是复杂的SELECT语句的做法几乎是不需要的;它往往会产生复杂的查询,这些查询的效率往往低于通过直接查询构建产生的查询的效率。The practice is to some degree based on the very early history of SQLAlchemy where the mapper() construct was meant to represent the primary querying interface; in modern usage, the Query object can be used to construct virtually any SELECT statement, including complex composites, and should be favored over the “map-to-selectable” approach.

一个类的多个映射器

在现代的SQLAlchemy,一个特定的类只由一个所谓的主 T0>映射器在一个时间被映射。这个映射器涉及三个主要的功能领域:映射类的查询,持久性和检测。主要映射器的基本原理与mapper()修改类本身,不仅将其持久化到一个特定的Table,还包括instrumenting多于一个的映射器不可能同时与一个类相关联,因为只有一个映射器可以实际地处理这个类。

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

在现代SQLAlchemy中,非主映射器的使用非常有限,因为可以直接使用Query对象来完成从子查询或其他复合语句加载类的任务。

对于非主映射器,实际上只有一个用例,那就是我们希望为这样一个映射器建立一个relationship();这在罕见的和高级的情况下非常有用,我们的关系试图通过使用许多表和/或连接来连接两个类。这种模式的一个例子是Relationship to Non Primary Mapper

至于实际上可以在不同场景下完全坚持不同表的类的用例,早期版本的SQLAlchemy提供了一个从Hibernate中调用的特性,称为“实体名称”特性。但是,一旦映射类本身成为SQL表达式构造的源,此用例在SQLAlchemy中就变得不可行了;也就是说,类的属性本身直接链接到映射的表列。该功能被删除,取而代之的是一个简单的面向配方的方法来完成这个任务没有任何歧义的仪器 - 创建新的子类,每个个别映射。这种模式现在可以在实体名称中作为配方使用。