组合列类型
列集可以与单个用户定义的数据类型相关联。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/y1
和 x2/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 Name | Description |
---|---|
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)