组合列类型

优质
小牛编辑
130浏览
2023-12-01

列集可以与单个用户定义的数据类型相关联。ORM提供了一个单独的属性,该属性表示使用您提供的类的列组。

一个简单的示例将列对表示为 Point 对象。 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 __repr__(self):
        return "Point(x=%r, y=%r)" % (self.x, self.y)

    def __eq__(self, other):
        return isinstance(other, Point) and \
            other.x == self.x and \
            other.y == self.y

    def __ne__(self, other):
        return not self.__eq__(other)

自定义数据类型类的要求是,它具有一个构造函数,该构造函数接受与其列格式相对应的位置参数,并提供一个方法 __composite_values__() 它按照对象基于列的属性的顺序,以列表或元组的形式返回对象的状态。它还应该提供足够的 __eq__()__ne__() 测试两个实例是否相等的方法。

我们将创建到表的映射 vertices ,表示两点 x1/y1x2/y2 . 这些通常创建为 Column 物体。然后, composite() 函数用于通过 Point 班级:

from sqlalchemy import Column, Integer
from sqlalchemy.orm import composite
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Vertex(Base):
    __tablename__ = 'vertices'

    id = Column(Integer, primary_key=True)
    x1 = Column(Integer)
    y1 = Column(Integer)
    x2 = Column(Integer)
    y2 = Column(Integer)

    start = composite(Point, x1, y1)
    end = composite(Point, x2, y2)

上面的经典映射将定义每个 composite() 对照现有表格:

mapper_registry.map_imperatively(Vertex, vertices_table, properties={
    'start':composite(Point, vertices_table.c.x1, vertices_table.c.y1),
    'end':composite(Point, vertices_table.c.x2, vertices_table.c.y2),
})

我们现在可以坚持和使用 Vertex 实例以及对它们的查询,使用 .start.end 特殊属性 Point 实例:

>>> v = Vertex(start=Point(3, 4), end=Point(5, 6))
>>> session.add(v)
>>> q = session.query(Vertex).filter(Vertex.start == Point(3, 4))
sql>>> print(q.first().start)
BEGIN (implicit)
INSERT INTO vertices (x1, y1, x2, y2) VALUES (?, ?, ?, ?)
(3, 4, 5, 6)
SELECT vertices.id AS vertices_id,
        vertices.x1 AS vertices_x1,
        vertices.y1 AS vertices_y1,
        vertices.x2 AS vertices_x2,
        vertices.y2 AS vertices_y2
FROM vertices
WHERE vertices.x1 = ? AND vertices.y1 = ?
 LIMIT ? OFFSET ?
(3, 4, 1, 0)
Point(x=3, y=4)
Object NameDescription

composite(class_, *attrs, **kwargs)

返回用于映射器的基于复合列的属性。

function sqlalchemy.orm.composite(class_, *attrs, **kwargs)

对现有组合值的就地更改不会自动跟踪。相反,复合类需要显式地向其父对象提供事件。通过使用 MutableComposite mixin,它使用事件将每个用户定义的复合对象与所有父关联关联起来。请参阅中的示例 确定复合材料的易变性 .

重新定义复合材料的比较操作

默认情况下,“等于”比较操作会生成一个和,其中所有相应的列彼此相等。可以使用 comparator_factory 参数 composite() ,其中我们指定了一个自定义 Comparator 类来定义现有或新操作。下面我们演示“大于”运算符,实现与基“大于”相同的表达式:

from sqlalchemy.orm.properties import CompositeProperty
from sqlalchemy import sql

class PointComparator(CompositeProperty.Comparator):
    def __gt__(self, other):
        """redefine the 'greater than' operation"""

        return sql.and_(*[a>b for a, b in
                          zip(self.__clause_element__().clauses,
                              other.__composite_values__())])

class Vertex(Base):
    ___tablename__ = 'vertices'

    id = Column(Integer, primary_key=True)
    x1 = Column(Integer)
    y1 = Column(Integer)
    x2 = Column(Integer)
    y2 = Column(Integer)

    start = composite(Point, x1, y1,
                        comparator_factory=PointComparator)
    end = composite(Point, x2, y2,
                        comparator_factory=PointComparator)

套料复合材料

复合对象可以定义为在简单的嵌套方案中工作,方法是将复合类中的行为重新定义为根据需要工作,然后通常将复合类映射到单个列的完整长度。通常,可以方便地为用户定义的使用定义单独的构造函数,并从行使用中生成。下面我们重组 Vertex 类自身是复合对象,然后将其映射到类 HasVertex ::

from sqlalchemy.orm import composite

class Point(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __composite_values__(self):
        return self.x, self.y

    def __repr__(self):
        return "Point(x=%r, y=%r)" % (self.x, self.y)

    def __eq__(self, other):
        return isinstance(other, Point) and \
            other.x == self.x and \
            other.y == self.y

    def __ne__(self, other):
        return not self.__eq__(other)

class Vertex(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end

    @classmethod
    def _generate(self, x1, y1, x2, y2):
        """generate a Vertex from a row"""
        return Vertex(
            Point(x1, y1),
            Point(x2, y2)
        )

    def __composite_values__(self):
        return \
            self.start.__composite_values__() + \
            self.end.__composite_values__()

class HasVertex(Base):
    __tablename__ = 'has_vertex'
    id = Column(Integer, primary_key=True)
    x1 = Column(Integer)
    y1 = Column(Integer)
    x2 = Column(Integer)
    y2 = Column(Integer)

    vertex = composite(Vertex._generate, x1, y1, x2, y2)

然后我们可以使用上面的映射作为:

hv = HasVertex(vertex=Vertex(Point(1, 2), Point(3, 4)))

s.add(hv)
s.commit()

hv = s.query(HasVertex).filter(
    HasVertex.vertex == Vertex(Point(1, 2), Point(3, 4))).first()
print(hv.vertex.start)
print(hv.vertex.end)