sqlalchemy - 如何将带有子查询的查询转换为关系

时间:2012-04-17 14:48:10

标签: sqlalchemy relationship

在下面的代码中,我想将帐户中的all_holdings替换为名为holdings的属性,该属性返回desired_holdings(代表可以随时间变化的最新已知数量的馆藏) )。我无法弄清楚如何构建对关系的调用。

此外,我还要感谢对模式的适当性的任何评论(将历史数据保存在单个表中并使用最大日期子查询来获取最新信息),以及更好的替代方案或查询的改进。

from sqlalchemy import Column, Integer, String, Date, DateTime, REAL, ForeignKey, func
from sqlalchemy.orm import relationship, aliased
from sqlalchemy.sql.operators import and_, eq
from sqlalchemy.ext.declarative import declarative_base
from db import session
import datetime
import string

Base = declarative_base()

class MySQLSettings(object):
    __table_args__ = {'mysql_engine':'InnoDB'}

class Account(MySQLSettings, Base):
    __tablename__ = 'account'
    id = Column(Integer, primary_key=True)
    name = Column(String(64))
    all_holdings = relationship('Holding', backref='account')

    def desired_holdings(self):
        max_date_subq = session.query(Holding.account_id.label('account_id'),
                                      Holding.stock_id.label('stock_id'),
                                      func.max(Holding.as_of).label('max_as_of')). \
                                      group_by(Holding.account_id, Holding.stock_id).subquery()

        desired_query = session.query(Holding).join(Account,
                                                    Account.id==account.id).join(max_date_subq).\
                                                    filter(max_date_subq.c.account_id==account.id).\
                                                    filter(Holding.as_of==max_date_subq.c.max_as_of).\
                                                    filter(Holding.account_id==max_date_subq.c.account_id).\
                                                    filter(Holding.stock_id==max_date_subq.c.stock_id)

        return desired_query.all()

    def __init__(self, name):
        self.name = name

class Stock(MySQLSettings, Base):
    __tablename__ = 'stock'
    id = Column(Integer, primary_key=True)
    name = Column(String(64))

    def __init__(self, name):
        self.name = name

class Holding(MySQLSettings, Base):
    __tablename__ = 'holding'
    id = Column(Integer, primary_key=True)
    account_id = Column(Integer, ForeignKey('account.id'), nullable=False)
    stock_id = Column(Integer, ForeignKey('stock.id'), nullable=False)
    quantity = Column(REAL)
    as_of = Column(Date)

    stock = relationship('Stock')

    def __str__(self):
        return "Holding(%f, '%s' '%s')"%(self.quantity, self.stock.name, str(self.as_of))

    def __init__(self, account, stock, quantity, as_of):
        self.account_id = account.id
        self.stock_id = stock.id
        self.quantity = quantity
        self.as_of = as_of

if __name__ == "__main__":
    ibm = Stock('ibm')
    session.add(ibm)
    account = Account('a')
    session.add(account)
    session.flush()
    session.add_all([ Holding(account, ibm, 100, datetime.date(2001, 1, 1)),
                      Holding(account, ibm, 200, datetime.date(2001, 1, 3)),
                      Holding(account, ibm, 300, datetime.date(2001, 1, 5)) ])
    session.commit()

    print "All holdings by relation:\n\t", \
        string.join([ str(h) for h in account.all_holdings ], "\n\t")

    print "Desired holdings query:\n\t", \
        string.join([ str(h) for h in account.desired_holdings() ], "\n\t")

运行时的结果是:

All holdings by relation:
    Holding(100.000000, 'ibm' '2001-01-01')
    Holding(200.000000, 'ibm' '2001-01-03')
    Holding(300.000000, 'ibm' '2001-01-05')
Desired holdings query:
    Holding(300.000000, 'ibm' '2001-01-05')

1 个答案:

答案 0 :(得分:2)

在我发布到sqlalchemy谷歌小组后,迈克尔拜耳提供的答案:

  

desired_holdings()查询非常复杂,我没有通过尝试获得relationship()来获得胜利。 relationship()用于维护两个类之间的持久性,而不是报告技术(并且其中包含max()/ group_by的任何内容都指的是报告)。

     

我会将@property放在desired_holdings之上,使用object_session(self)来获取“session”,然后完成。

查看有关query-enabled properties的更多信息。