SQLAlchemy ORM:指向关系的第一个元素的代理属性?

时间:2015-02-11 14:26:45

标签: python mysql orm sqlalchemy

从SQLAlchemy文档中的many-to-many relationship example开始,我想添加一个属性first_child,它将返回由关系定义的children的第一个子节点。 first_child属性需要在association_proxy属性定义中使用,例如下面的first_child_id

from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()

association_table = Table('association', Base.metadata,
    Column('left_id', Integer, ForeignKey('left.id')),
    Column('right_id', Integer, ForeignKey('right.id'))
)

class Parent(Base):
    __tablename__ = 'left'
    id = Column(Integer, primary_key=True)
    children = relationship("Child", secondary=association_table)
    first_child = ???
    first_child_id = association_proxy('first_child', 'id')

class Child(Base):
    __tablename__ = 'right'
    id = Column(Integer, primary_key=True)

我认为我需要将first_child声明为hybrid_property or a column_property,但我不知道如何返回第一个元素。

除了first_child之外,我还需要last_child和相关的last_child_id属性。

我正在使用SQLAlchemy和MySQL数据库。

2 个答案:

答案 0 :(得分:0)

如果你需要的是最小start_time 最大end_time ,那么我只会为这些列使用column_property

association_table = Table(
    'association', Base.metadata,
    Column('left_id', Integer, ForeignKey('left.id')),
    Column('right_id', Integer, ForeignKey('right.id'))
)


class Child(Base):
    __tablename__ = 'right'
    id = Column(Integer, primary_key=True)
    start_time = Column(DateTime)
    end_time = Column(DateTime)


class Parent(Base):
    __tablename__ = 'left'
    id = Column(Integer, primary_key=True)
    children = relationship(
        "Child",
        secondary=association_table,
        backref="parents",
    )

    min_start_time = column_property(
        select([Child.start_time.label("min_start_time")])
        .where(Child.id == association_table.c.right_id)
        .where(id == association_table.c.left_id)
        .order_by(Child.start_time.asc())
        .limit(1)
    )

    max_end_time = column_property(
        select([Child.end_time.label("max_end_time")])
        .where(Child.id == association_table.c.right_id)
        .where(id == association_table.c.left_id)
        .order_by(Child.end_time.desc())
        .limit(1)
        .as_scalar()
    )

但是如果您需要关系中不止一个这样的特殊列,那么使用hybrid_property可能会更有效。

答案 1 :(得分:0)

association_proxy 的问题在于您不能在 hybrid_properties 上使用它(或者至少不能直接使用)。

一个简单的解决方案可能是使用 property 装饰器,它会在已经加载的实例上进行评估:


class Parent(Base):
    __tablename__ = 'left'
    id = Column(Integer, primary_key=True)
    children = relationship("Child", secondary=association_table)

    @property
    def first_child(self):
        return children[0]

但是,您将无法在查询中使用它,并且它是“只读”属性。有关 SQLAlchemy plain-descriptor 的更多信息。