4

私はsqlalchemy(実際にはFlask-sqlalchemy、したがってすべてのdb.*)を持っており、関連する「Votes」の平均vote.valueで「Things」をソートできるようにしたいと考えています。投票の値は 0 ~ 100 です。

sqlalchemy が average_vote_value @attribute を sql に変換しようとして失敗するという問題に遭遇したので、おそらくハイブリッドを使用する必要があることがわかりました。

ただし、この場合、それがどのように行われるかはわかりません。誰でも助けてもらえますか?

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)
4

1 に答える 1

16

結局のところ、必要な結果を SQL クエリとして取得する方法を検討する必要があります。「ハイブリッド、Python、プロパティ」などの観点からのみ考えることはできません。これらの手法を使用して結果を取得しますが、そこに導くのは SQL の動作方法です。それでは、Postgresql を使用してみましょう。ほとんどのデータベースに組み込まれている AVG 関数が組み込まれています。Thing から Vote に JOIN する必要があります。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)]
于 2012-06-07T01:09:10.997 に答える