作为映射属性的SQL表达式¶

映射类上的属性可以链接到SQL表达式,SQL表达式可用于查询。

使用杂交种

将相对简单的SQL表达式链接到类的最简单和最灵活的方法是使用一个在本节中描述的所谓的“混合属性”。 混合属性 . 混合提供了一个同时在Python和SQL表达式级别工作的表达式。例如,下面我们映射一个类 User ,包含属性 firstnamelastname 包括一个混合动力车 fullname ,这是两个字符串的串联:

from sqlalchemy.ext.hybrid import hybrid_property

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))

    @hybrid_property
    def fullname(self):
        return self.firstname + " " + self.lastname

上面, fullname 属性在实例和类级别都被解释,以便它可以从实例中使用::

some_user = session.query(User).first()
print(some_user.fullname)

以及在查询中可用:

some_user = session.query(User).filter(User.fullname == "John Smith").first()

字符串连接示例是一个简单的示例,其中python表达式可以在实例和类级别上实现双重目的。通常,必须将SQL表达式与Python表达式区分开来,后者可以使用 hybrid_property.expression() . 下面我们将说明在混合动力系统中需要存在条件的情况,使用 if python和 sql.expression.case() SQL表达式的构造::

from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import case

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))

    @hybrid_property
    def fullname(self):
        if self.firstname is not None:
            return self.firstname + " " + self.lastname
        else:
            return self.lastname

    @fullname.expression
    def fullname(cls):
        return case([
            (cls.firstname != None, cls.firstname + " " + cls.lastname),
        ], else_ = cls.lastname)

使用列属性

这个 orm.column_property() 函数可用于以类似于定期映射的方式映射SQL表达式 Column . 使用此技术,将在加载时与所有其他列映射属性一起加载属性。在某些情况下,这是使用混合的一个优势,因为值可以与对象的父行同时预先加载,特别是当表达式链接到其他表(通常作为相关子查询)以访问通常在已加载的对象上不可用的数据时。

使用的缺点 orm.column_property() 因为SQL表达式包括表达式必须与为类整体发出的select语句兼容,并且在使用 orm.column_property() 来自声明性混合。

我们的“全名”示例可以用 orm.column_property() 如下:

from sqlalchemy.orm import column_property

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))
    fullname = column_property(firstname + " " + lastname)

也可以使用相关的子查询。下面我们用 select() 构造以创建一个将 Address 特定对象的可用对象 User ::

from sqlalchemy.orm import column_property
from sqlalchemy import select, func
from sqlalchemy import Column, Integer, String, ForeignKey

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('user.id'))

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    address_count = column_property(
        select([func.count(Address.id)]).\
            where(Address.user_id==id).\
            correlate_except(Address)
    )

在上面的示例中,我们定义了 select() 构造如下:

select([func.count(Address.id)]).\
    where(Address.user_id==id).\
    correlate_except(Address)

上述语句的含义是,选择 Address.id 其中的行 Address.user_id 列等于 id ,在 User 类是 Column 已命名 id (注意 id 也是Python内置函数的名称,这不是我们想在这里使用的——如果我们不在 User 类定义,我们将使用 User.id

这个 select.correlate_except() 指令指示此的From子句中的每个元素 select() 可以从“从”列表中省略(即,与封闭的select语句相关 User )除了对应于 Address . 这不是严格必要的,但可以防止 Address 如果在 UserAddress 选择语句所针对的表 Address 是嵌套的。

如果导入问题阻止 column_property() 通过与类内联定义,可以在两个类都配置之后将其分配给类。在声明性中,这具有调用 Mapper.add_property() 要在事实之后添加其他属性,请执行以下操作:

User.address_count = column_property(
        select([func.count(Address.id)]).\
            where(Address.user_id==User.id)
    )

对于多对多关系,使用 and_() 要将关联表的字段连接到关系中的两个表,请使用经典映射进行说明:

from sqlalchemy import and_

mapper(Author, authors, properties={
    'book_count': column_property(
                        select([func.count(books.c.id)],
                            and_(
                                book_authors.c.author_id==authors.c.id,
                                book_authors.c.book_id==books.c.id
                            )))
    })

使用普通描述符

如果SQL查询比 orm.column_property()hybrid_property 必须发出can-provide,可以使用作为属性访问的常规python函数,假定表达式只需要在已加载的实例上可用。这个函数是用python自己的 @property decorator将其标记为只读属性。在函数中, object_session() 用于定位 Session 对应于当前对象,该对象随后用于发出查询::

from sqlalchemy.orm import object_session
from sqlalchemy import select, func

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    firstname = Column(String(50))
    lastname = Column(String(50))

    @property
    def address_count(self):
        return object_session(self).\
            scalar(
                select([func.count(Address.id)]).\
                    where(Address.user_id==self.id)
            )

普通描述符方法作为最后一种手段很有用,但在通常情况下,它的性能不如混合和列属性方法,因为它需要在每次访问时发出一个SQL查询。

作为映射属性的查询时间SQL表达式

使用时 Session.query() 我们不仅可以指定映射的实体,还可以指定特殊的SQL表达式。假设一个班 A 具有整数属性 .x.y ,我们可以查询 A 对象,以及 .x.y 如下:

q = session.query(A, A.x + A.y)

上面的查询返回窗体的元组 (A object, integer) .

存在可应用临时 A.x + A.y 返回的表达式 A 对象,而不是作为单独的元组项;这是 with_expression() 查询选项与 query_expression() 属性映射。类被映射为包含一个占位符属性,其中可以应用任何特定的SQL表达式::

from sqlalchemy.orm import query_expression

class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)
    x = Column(Integer)
    y = Column(Integer)

    expr = query_expression()

然后我们可以查询类型为的对象 A ,应用要填充到的任意SQL表达式 A.expr ::

from sqlalchemy.orm import with_expression
q = session.query(A).options(
    with_expression(A.expr, A.x + A.y))

这个 query_expression() 地图绘制有以下注意事项:

1.2 新版功能.