简体   繁体   English

如何定义代表集合中最新 object 的 SQLAlchemy 关系?

[英]How do I define a SQLAlchemy relation representing the latest object in a collection?

I have a SQLAlchemy model with a one-to-many relationship between table x and table y .我有一个 SQLAlchemy model 与表x和表y之间的一对多关系。 The record (if any) with the greatest id in table y where y.x_id = x.id is special.yy.x_id = x.idid最大的记录(如果有)是特殊的。 Class X and class Y map tables x and y . Class X和 class Y map 表xy

I know how to define X.all_y ( ORDER BY y.id ).我知道如何定义X.all_y ( ORDER BY y.id )。 How do I define X.latest_y equivalent to X.all_y[-1] ?如何定义X.latest_y等同于X.all_y[-1]

the purely relational way to do it requires using a subquery to get the "latest" or "max" value, correlated to the parent, then equating that with the members of the collection. 纯粹的关系方式要求使用子查询来获取与父级相关的“最新”或“最大”值,然后将其与集合的成员等同。 It means you'll get best results if you put an index on the column that determines "the latest": 这意味着如果您在确定“最新”的列上放置索引,您将获得最佳结果:

from sqlalchemy import *
from sqlalchemy.orm import *

engine = create_engine('sqlite:///:memory:', echo='debug')

m = MetaData()

parent = Table('parent', m, 
                Column('id', Integer, primary_key=True)
)

child = Table('child', m, 
                Column('id', Integer, primary_key=True),
                Column('parent_id', Integer, ForeignKey('parent.id')),
                Column('sortkey', Integer)
                )

m.create_all(engine)

class Parent(object):
    def __init__(self, children):
        self.all_c = children

class Child(object):
    def __init__(self, sortkey):
        self.sortkey = sortkey

latest_c = select([func.max(child.c.sortkey)]).\
                where(child.c.parent_id==parent.c.id).\
                correlate(parent).\
                as_scalar()

mapper(Parent, parent, properties={
    'all_c':relation(Child),
    'latest_c':relation(Child, 
                            primaryjoin=and_(
                                child.c.sortkey==latest_c, 
                                child.c.parent_id==parent.c.id
                            ),
                            uselist=False
    )
})

mapper(Child, child)

session = sessionmaker(engine)()

p1, p2, p3 = Parent([Child('a'), Child('b'), Child('c')]), \
                Parent([Child('b'), Child('c')]),\
                Parent([Child('f'), Child('g'), Child('c')])

session.add_all([p1, p2, p3])
session.commit()

assert p1.latest_c.sortkey == 'c'
assert p2.latest_c.sortkey == 'c'
assert p3.latest_c.sortkey == 'g'

Alternatively, you can on some platforms use LIMIT, which can produce faster results since you avoid the aggregation and can join the collection item on its primary key: 或者,您可以在某些平台上使用LIMIT,这可以产生更快的结果,因为您可以避免聚合并可以在其主键上加入集合项:

latest_c = select([child.c.id]).\
                where(child.c.parent_id==parent.c.id).\
                order_by(child.c.sortkey.desc()).\
                limit(1).\
                correlate(parent).\
                as_scalar()

mapper(Parent, parent, properties={
    'all_c':relation(Child),
    'latest_c':relation(Child, 
                            primaryjoin=and_(
                                child.c.id==latest_c, 
                                child.c.parent_id==parent.c.id
                            ),
                            uselist=False
    )
})

Here is a version of @zzzeek 's answer that uses declarative rather than imperative mapping, using declared_attr to insert the relationship into the parent's __mapper_args__ .这是@zzzeek答案的一个版本,它使用声明式而不是命令式映射,使用declared_attr将关系插入到父级的__mapper_args__中。

import sqlalchemy as sa
from sqlalchemy import orm

Base = orm.declarative_base()


class Child(Base):
    __tablename__ = 'children'

    id = sa.Column(sa.Integer, primary_key=True)
    sortkey = sa.Column(sa.Integer, nullable=False)
    parent_id = sa.Column(sa.Integer, sa.ForeignKey('parents.id'))
    parent = orm.relationship('Parent', back_populates='children')


class Parent(Base):
    __tablename__ = 'parents'

    id = sa.Column(sa.Integer, primary_key=True)
    children = orm.relationship('Child', back_populates='parent')

    @orm.declared_attr
    def __mapper_args__(cls):
        children = Child.__table__
        most_recent_child = (
            sa.select(children.c.id)
            .where(children.c.parent_id == cls.id)
            .order_by(children.c.sortkey.desc())
            .limit(1)
            .correlate(cls.__table__)
            .scalar_subquery()
        )

        rel = orm.relation(
            Child,
            primaryjoin=sa.and_(
                Child.id == most_recent_child, Child.parent_id == cls.id
            ),
            uselist=False,
            viewonly=True,
        )
        return {'properties': {'latest_child': rel}}


# Build and test.
engine = sa.create_engine('sqlite://', echo=True, future=True)
Base.metadata.create_all(engine)
Session = orm.sessionmaker(engine, future=True)

with Session.begin() as s:
    children = [Child(sortkey=i) for i in range(1, 6)]
    parent = Parent(children=children)
    s.add(parent)

with Session() as s:
    w = s.scalars(sa.select(Parent)).first()
    assert w.latest_child.sortkey == 5, f'{w.latest_child.sortkey=}'
    assert len(w.children) == 5, f'{len(w.children)=}'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用SQLAlchemy,如何建立动态关系? - With SQLAlchemy, how do I make a dynamic relation? 如何在 sqlalchemy 中获得循环关系? - how do I get a circular relation working in sqlalchemy? 如何定义SQLAlchemy函数并将其用作表字段的默认值? - How do I define a SQLAlchemy function and use it as default for a table field? 在eve-sqlalchemy中如何查询嵌套集合中的资源? - How do I query resources in the nested collection in eve-sqlalchemy? 如果在 SQLAlchemy 中将“子”对象分配给“关系”,如何在“父”对象上设置属性? - How can I set attributes on a "parent" object if a "child" object is assigned to a "relation" in SQLAlchemy? 如何从SQLAlchemy中的对象访问相关的ForeignKey对象? - How do I access the related ForeignKey object from a object in SQLAlchemy? 如何在SQLAlchemy中定义(或模拟)包含不同类型的集合? - How to define (or simulate) a collection containing different types in SQLAlchemy? 如何在SQLAlchemy中指定一个条件,其中一个条件要求列为空? - How do I specify a relation in SQLAlchemy where one condition requires a column to be null? 如何在Pyarango中获得收集对象? - How do I get a collection object in pyarango? 如何在 SQLAlchemy 更新时更新相关对象? - How do I update related object on update in SQLAlchemy?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM