【问题标题】:Sqlalchemy hybrids炼金术杂种
【发布时间】:2012-06-02 10:02:18
【问题描述】:

我有一个 sqlalchemy(实际上是 Flask-sqlalchemy,因此是所有 db.*),我希望能够通过它们相关的“投票”的平均投票值对我的“事物”进行排序。投票的值是 0 到 100。

遇到了 sqlalchemy 想要将 average_vote_value @attribute 转换为 sql 的问题并且失败了,我发现我可能应该使用 hybrids

但是我无法弄清楚在这种情况下是如何完成的。有人可以帮忙吗?

class Thing(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))
    votes = db.relationship('Vote', backref='thing', lazy='dynamic')

    @hybrid_property
    def average_vote_value(self):
        '''average of vote.values'''
        values = [v.value for v in self.votes]
        try:
            return sum(scores) / len(values)
        except ZeroDivisionError:
            return 50 # the default value

    average_vote_value.expression
    def average_vote_value(cls):
        pass ### help ###


class Vote(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    thing_id = db.Column(db.Integer, db.ForeignKey('thing.id'))
    value = db.Column(db.Float, default=50.0)

【问题讨论】:

    标签: sqlalchemy hybrid


    【解决方案1】:

    归根结底,您需要考虑如何以 SQL 查询的形式获得您想要的结果。您不能只从“混合、python、属性”等方面来考虑它。虽然我们将使用这些技术来获得结果,但正是 SQL 的工作方式将我们引向了那里。所以让我们使用 Postgresql,它内置了大多数数据库都有的 AVG 功能。我们需要从 Thing JOIN 到 Vote,并且由于您要考虑 Thing 没有 Votes 的情况,因此需要 LEFT OUTER JOIN。混合表达式只是您想要的 SQL 表达式的语法助手,但归根结底您仍然需要拼出 SQL 需要的 JOIN:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.hybrid import hybrid_property
    from sqlalchemy.ext.declarative import declarative_base
    
    Base= declarative_base()
    
    class Thing(Base):
        __tablename__ = 'thing'
        id = Column(Integer, primary_key=True)
        name = Column(String(80))
        votes = relationship('Vote', backref='thing', lazy='dynamic')
    
        @hybrid_property
        def average_vote_value(self):
            '''average of vote.values'''
            values = [v.value for v in self.votes]
            try:
                return sum(values) / len(values)
            except ZeroDivisionError:
                return 50 # the default value
    
        @average_vote_value.expression
        def average_vote_value(cls):
            return func.coalesce(func.avg(Vote.value), 50)
    
    
    class Vote(Base):
        __tablename__ = 'vote'
        id = Column(Integer, primary_key=True)
        thing_id = Column(Integer, ForeignKey('thing.id'))
        value = Column(Float, default=50.0)
    
    e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
    Base.metadata.drop_all(e)
    Base.metadata.create_all(e)
    
    s = Session(e)
    
    s.add_all([
        Thing(name="thing1", votes=[
            Vote(value=5),
            Vote(value=7),
            Vote(value=7),
            Vote(value=8),
            Vote(value=8),
            Vote(value=12),
            Vote(value=2),
            Vote(value=15),
            Vote(value=10),
        ]),
        Thing(name="thing2", votes=[
            Vote(value=18),
            Vote(value=16),
            Vote(value=27),
            Vote(value=6),
            Vote(value=10),
        ]),
        Thing(name="thing3", votes=[])
    ]
    )
    s.commit()
    
    print s.query(Thing.name, Thing.average_vote_value).\
                outerjoin(Thing.votes).\
                group_by(Thing.name).all()
    

    输出(减去回声):

    [(u'thing3', 50.0), (u'thing1', 8.22222222222222), (u'thing2', 15.4)]
    

    【讨论】:

      猜你喜欢
      • 2016-10-02
      • 2016-09-27
      • 1970-01-01
      • 2014-10-12
      • 1970-01-01
      • 1970-01-01
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多