Как определить отношение SQLAlchemy, представляющее последний объект в коллекции? - PullRequest
4 голосов
/ 19 декабря 2009

У меня есть модель SQLAlchemy с отношением «один ко многим» между таблицей x и таблицей y. Запись (если есть) с наибольшим значением id в таблице y, где y.x_id = x.id является особенной. Класс X и класс Y таблицы таблиц x и y.

Я знаю, как определить X.all_y (ORDER BY y.id). Как определить X.latest_y эквивалентно X.all_y[-1]?

1 Ответ

5 голосов
/ 19 декабря 2009

чисто реляционный способ сделать это требует использования подзапроса, чтобы получить «последнее» или «максимальное» значение, соотнесенное с родительским, а затем приравнять его к членам коллекции. Это означает, что вы получите лучшие результаты, если поместите индекс в столбец, который определяет «последний»:

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'

В качестве альтернативы вы можете использовать на некоторых платформах 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
    )
})
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...