我有一个sqlalchemy(实际上是Flask-sqlalchemy因此所有的数据库.*),我希望能够通过他们与之相关的“投票”的平均投票价值来排序我的“事物”.投票的值为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)
解决方法
在一天结束时,您需要考虑如何获得您想要的结果作为SQL查询.你不能仅仅从“混合,python,属性”等方面来考虑它.虽然我们将使用这些技术来获得结果,但这是sql工作方式引领我们的方式.所以让我们使用Postgresql,它内置AVG功能,大多数数据库都有.我们将需要从Thing加入投票,因为你想要考虑Thing没有投票的情况,一个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',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=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),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)]