SqlAlchemy 2.0 中文文档(八十一)(4)

简介: SqlAlchemy 2.0 中文文档(八十一)

SqlAlchemy 2.0 中文文档(八十一)(3)https://developer.aliyun.com/article/1559911


复合类型

这是来自 Hibernate 阵营的一个例子。复合类型允许您定义一个由多个列(或一个列,如果您愿意)组成的自定义数据类型。让我们定义一个新类型,Point。存储一个 x/y 坐标:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __composite_values__(self):
        return self.x, self.y
    def __eq__(self, other):
        return other.x == self.x and other.y == self.y
    def __ne__(self, other):
        return not self.__eq__(other)

Point对象的定义方式是特定于自定义类型的;构造函数接受参数列表,并且__composite_values__()方法生成这些参数的序列。顺序将与我们的映射器匹配,我们马上就会看到。

让我们创建一个存储每行两个点的顶点表:

vertices = Table(
    "vertices",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("x1", Integer),
    Column("y1", Integer),
    Column("x2", Integer),
    Column("y2", Integer),
)

然后,映射它!我们将创建一个存储两个Point对象的Vertex对象:

class Vertex(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
mapper(
    Vertex,
    vertices,
    properties={
        "start": composite(Point, vertices.c.x1, vertices.c.y1),
        "end": composite(Point, vertices.c.x2, vertices.c.y2),
    },
)

一旦设置了您的复合类型,它就可以像任何其他类型一样使用:

v = Vertex(Point(3, 4), Point(26, 15))
session.save(v)
session.flush()
# works in queries too
q = session.query(Vertex).filter(Vertex.start == Point(3, 4))

如果您想定义映射属性在表达式中生成 SQL 子句的方式,请创建自己的sqlalchemy.orm.PropComparator子类,定义任何常见操作符(如__eq__()__le__()等),并将其发送到composite()。复合类型也可以作为主键,并且可在query.get()中使用:

# a Document class which uses a composite Version
# object as primary key
document = query.get(Version(1, "a"))
dynamic_loader()关系

一个返回所有读操作的实时Query对象的relation()。写操作仅限于append()remove(),对集合的更改在会话刷新之前不可见。此功能在“自动刷新”会话中特别方便,该会话会在每次查询之前刷新。

mapper(
    Foo,
    foo_table,
    properties={
        "bars": dynamic_loader(
            Bar,
            backref="foo",
            # <other relation() opts>
        )
    },
)
session = create_session(autoflush=True)
foo = session.query(Foo).first()
foo.bars.append(Bar(name="lala"))
for bar in foo.bars.filter(Bar.name == "lala"):
    print(bar)
session.commit()
新选项:undefer_group()eagerload_all()

一些方便的查询选项。undefer_group()将整个“延迟”列组标记为未延迟:

mapper(
    Class,
    table,
    properties={
        "foo": deferred(table.c.foo, group="group1"),
        "bar": deferred(table.c.bar, group="group1"),
        "bat": deferred(table.c.bat, group="group1"),
    },
)
session.query(Class).options(undefer_group("group1")).filter(...).all()

eagerload_all()设置一系列属性在一次遍历中急加载:

mapper(Foo, foo_table, properties={"bar": relation(Bar)})
mapper(Bar, bar_table, properties={"bat": relation(Bat)})
mapper(Bat, bat_table)
# eager load bar and bat
session.query(Foo).options(eagerload_all("bar.bat")).filter(...).all()
新的集合 API

集合不再由 {{{InstrumentedList}}}  代理进行代理,并且对成员、方法和属性的访问是直接的。装饰器现在拦截进入和离开集合的对象,并且现在可以轻松地编写一个自定义集合类来管理自己的成员。灵活的装饰器还取代了  0.3 版本中自定义集合的命名方法接口,允许任何类轻松地适应用作集合容器。

基于字典的集合现在更易于使用,并且完全类似于dict。对dict来说,不再需要更改__iter__,新的内置dict类型涵盖了许多需求:

# use a dictionary relation keyed by a column
relation(Item, collection_class=column_mapped_collection(items.c.keyword))
# or named attribute
relation(Item, collection_class=attribute_mapped_collection("keyword"))
# or any function you like
relation(Item, collection_class=mapped_collection(lambda entity: entity.a + entity.b))

现有的 0.3 版本类似dict和自由对象衍生的集合类将需要更新到新的 API。在大多数情况下,这只是在类定义中添加几个装饰器的问题。

来自外部表/子查询的映射关系

该功能在 0.3 版本中悄悄出现,但由于更好地能够将针对表的子查询转换为针对该表的别名的子查询而得到改进,在 0.4 版本中得到改进;这对于贪婪加载、查询中的别名连接等非常重要。它减少了在只需要添加一些额外列或子查询时创建针对选择语句的映射器的需求:

mapper(
    User,
    users,
    properties={
        "fullname": column_property(
            (users.c.firstname + users.c.lastname).label("fullname")
        ),
        "numposts": column_property(
            select([func.count(1)], users.c.id == posts.c.user_id)
            .correlate(users)
            .label("posts")
        ),
    },
)

典型的查询看起来像:

SELECT  (SELECT  count(1)  FROM  posts  WHERE  users.id  =  posts.user_id)  AS  count,
users.firstname  ||  users.lastname  AS  fullname,
users.id  AS  users_id,  users.firstname  AS  users_firstname,  users.lastname  AS  users_lastname
FROM  users  ORDER  BY  users.oid
更新/插入中嵌入的 SQL 子句

对于嵌入在flush()期间的 UPDATE 或 INSERT 中的 SQL 子句的内联执行:

myobject.foo = mytable.c.value + 1
user.pwhash = func.md5(password)
order.hash = text("select hash from hashing_table")

在操作后设置了延迟加载器的列属性,以便在下次访问时发出加载新值的 SQL。

自引用和循环贪婪加载

由于我们的别名技术已经提高,relation()可以沿着同一张表任意次数进行连接;您告诉它您想要多深。让我们更清楚地展示自引用的TreeNode

nodes = Table(
    "nodes",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("parent_id", Integer, ForeignKey("nodes.id")),
    Column("name", String(30)),
)
class TreeNode(object):
    pass
mapper(
    TreeNode,
    nodes,
    properties={"children": relation(TreeNode, lazy=False, join_depth=3)},
)

那么,当我们说:

create_session().query(TreeNode).all()

? 在父级的别名上三级深度的连接:

SELECT
nodes_3.id  AS  nodes_3_id,  nodes_3.parent_id  AS  nodes_3_parent_id,  nodes_3.name  AS  nodes_3_name,
nodes_2.id  AS  nodes_2_id,  nodes_2.parent_id  AS  nodes_2_parent_id,  nodes_2.name  AS  nodes_2_name,
nodes_1.id  AS  nodes_1_id,  nodes_1.parent_id  AS  nodes_1_parent_id,  nodes_1.name  AS  nodes_1_name,
nodes.id  AS  nodes_id,  nodes.parent_id  AS  nodes_parent_id,  nodes.name  AS  nodes_name
FROM  nodes  LEFT  OUTER  JOIN  nodes  AS  nodes_1  ON  nodes.id  =  nodes_1.parent_id
LEFT  OUTER  JOIN  nodes  AS  nodes_2  ON  nodes_1.id  =  nodes_2.parent_id
LEFT  OUTER  JOIN  nodes  AS  nodes_3  ON  nodes_2.id  =  nodes_3.parent_id
ORDER  BY  nodes.oid,  nodes_1.oid,  nodes_2.oid,  nodes_3.oid

注意这些漂亮干净的别名。连接不在乎它是针对同一即时表还是针对某个其他对象,然后又回到开头。当指定了join_depth时,任何类型的贪婪加载都可以在自身上循环回来。当不存在时,贪婪加载在碰到循环时会自动停止。

复合类型

这是 Hibernate 阵营中的一个特点。复合类型允许您定义一个由多个列(或一个列,如果您想要的话)组成的自定义数据类型。让我们定义一个新类型,Point。存储一个 x/y 坐标:

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __composite_values__(self):
        return self.x, self.y
    def __eq__(self, other):
        return other.x == self.x and other.y == self.y
    def __ne__(self, other):
        return not self.__eq__(other)

Point 对象的定义方式是特定于自定义类型的;构造函数接受一个参数列表,并且__composite_values__()方法生成这些参数的序列。稍后我们将看到,顺序将与我们的映射器相匹配。

让我们创建一个存储每行两个点的顶点表:

vertices = Table(
    "vertices",
    metadata,
    Column("id", Integer, primary_key=True),
    Column("x1", Integer),
    Column("y1", Integer),
    Column("x2", Integer),
    Column("y2", Integer),
)

然后,映射它!我们将创建一个存储两个Point对象的Vertex对象:

class Vertex(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
mapper(
    Vertex,
    vertices,
    properties={
        "start": composite(Point, vertices.c.x1, vertices.c.y1),
        "end": composite(Point, vertices.c.x2, vertices.c.y2),
    },
)

一旦设置了复合类型,它就可以像任何其他类型一样使用:

v = Vertex(Point(3, 4), Point(26, 15))
session.save(v)
session.flush()
# works in queries too
q = session.query(Vertex).filter(Vertex.start == Point(3, 4))

如果您想要定义映射属性在表达式中生成 SQL 子句的方式,请创建自己的 sqlalchemy.orm.PropComparator 子类,定义任何常见操作符(如 __eq__()__le__() 等),并将其发送到 composite()。组合类型也可以作为主键,并且可在 query.get() 中使用:

# a Document class which uses a composite Version
# object as primary key
document = query.get(Version(1, "a"))
dynamic_loader() 关系

relation() 返回一个用于所有读取操作的实时 Query 对象。写操作仅限于 append()remove(),对集合的更改在会话刷新之前不可见。这个特性在“自动刷新”会话中特别方便,在每次查询之前都会刷新。

mapper(
    Foo,
    foo_table,
    properties={
        "bars": dynamic_loader(
            Bar,
            backref="foo",
            # <other relation() opts>
        )
    },
)
session = create_session(autoflush=True)
foo = session.query(Foo).first()
foo.bars.append(Bar(name="lala"))
for bar in foo.bars.filter(Bar.name == "lala"):
    print(bar)
session.commit()
新选项:undefer_group()eagerload_all()

一些方便的查询选项。undefer_group() 将整个“延迟”列组标记为未延迟:

mapper(
    Class,
    table,
    properties={
        "foo": deferred(table.c.foo, group="group1"),
        "bar": deferred(table.c.bar, group="group1"),
        "bat": deferred(table.c.bat, group="group1"),
    },
)
session.query(Class).options(undefer_group("group1")).filter(...).all()

eagerload_all() 一次性设置一系列属性为急加载:

mapper(Foo, foo_table, properties={"bar": relation(Bar)})
mapper(Bar, bar_table, properties={"bat": relation(Bat)})
mapper(Bat, bat_table)
# eager load bar and bat
session.query(Foo).options(eagerload_all("bar.bat")).filter(...).all()
新的集合 API

集合不再由 InstrumentedList 代理代理,并且对成员、方法和属性的访问是直接的。装饰器现在拦截进入和离开集合的对象,并且现在可以轻松编写一个自定义的集合类来管理其自己的成员资格。灵活的装饰器也替代了 0.3 版本中自定义集合的命名方法接口,允许任何类容易地被调整为用作集合容器。

基于字典的集合现在更易于使用,并完全类似于 dict。不再需要更改 __iter__ 来使用 dict,并且新的内置 dict 类型满足了许多需求:

# use a dictionary relation keyed by a column
relation(Item, collection_class=column_mapped_collection(items.c.keyword))
# or named attribute
relation(Item, collection_class=attribute_mapped_collection("keyword"))
# or any function you like
relation(Item, collection_class=mapped_collection(lambda entity: entity.a + entity.b))

现有的 0.3 版本类似于 dict 和自由形式对象派生的集合类需要更新到新的 API。在大多数情况下,这只是简单地向类定义中添加几个装饰器。

来自外部表/子查询的映射关系

这个特性在 0.3 中悄然出现,但在 0.4 中得到了改进,这要归功于更好地将针对表的子查询转换为针对该表的别名的子查询的能力;这对于急加载、查询中的别名连接等非常重要。它减少了在只需要添加一些额外列或子查询时创建针对选择语句的映射器的必要性:

mapper(
    User,
    users,
    properties={
        "fullname": column_property(
            (users.c.firstname + users.c.lastname).label("fullname")
        ),
        "numposts": column_property(
            select([func.count(1)], users.c.id == posts.c.user_id)
            .correlate(users)
            .label("posts")
        ),
    },
)

典型的查询看起来像这样:

SELECT  (SELECT  count(1)  FROM  posts  WHERE  users.id  =  posts.user_id)  AS  count,
users.firstname  ||  users.lastname  AS  fullname,
users.id  AS  users_id,  users.firstname  AS  users_firstname,  users.lastname  AS  users_lastname
FROM  users  ORDER  BY  users.oid


SqlAlchemy 2.0 中文文档(八十一)(5)https://developer.aliyun.com/article/1559920

相关文章
|
3月前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(六十四)(3)
SqlAlchemy 2.0 中文文档(六十四)
29 0
|
3月前
|
关系型数据库 数据库连接 数据库
SqlAlchemy 2.0 中文文档(三十九)(1)
SqlAlchemy 2.0 中文文档(三十九)
32 0
|
3月前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(六十四)(1)
SqlAlchemy 2.0 中文文档(六十四)
34 0
|
3月前
|
SQL 数据库连接 数据库
SqlAlchemy 2.0 中文文档(五十五)(2)
SqlAlchemy 2.0 中文文档(五十五)
37 1
|
3月前
|
SQL 数据库连接 API
SqlAlchemy 2.0 中文文档(五十五)(3)
SqlAlchemy 2.0 中文文档(五十五)
53 1
|
3月前
|
API 数据库 Python
SqlAlchemy 2.0 中文文档(五十五)(6)
SqlAlchemy 2.0 中文文档(五十五)
38 1
|
3月前
|
SQL 缓存 编译器
SqlAlchemy 2.0 中文文档(五十五)(4)
SqlAlchemy 2.0 中文文档(五十五)
36 1
|
3月前
|
SQL 安全 数据库连接
SqlAlchemy 2.0 中文文档(五十五)(5)
SqlAlchemy 2.0 中文文档(五十五)
36 1
|
3月前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(五十五)(1)
SqlAlchemy 2.0 中文文档(五十五)
37 1
|
3月前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(八十一)(1)
SqlAlchemy 2.0 中文文档(八十一)
28 2