SQLAlchemy:如何根据计算值使用order_by?

时间:2015-05-01 14:41:43

标签: python sqlalchemy flask-sqlalchemy

所以我有以下数据库模型:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    nickname = db.Column(db.String(64), nullable=False, unique=True)
    ...
    tasks = db.relationship('Task', backref='author', lazy='dynamic')

    def number_of_tries(self):
        return Task.query.filter_by(user_id=self.id).count()

    def correct_answers(self):
        return Task.query.filter_by(user_id=self.id).filter_by(correct=True).count()

    def percentage(self):
        try:
            return '{:.2%}'.format(self.correct_answers()/self.number_of_tries())
        except ZeroDivisionError:
            return None

    def __repr__(self):
        return '<User %r>' % (self.nickname)


class Task(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    task_id = db.Column(db.Integer)
    correct = db.Column(db.Boolean)
    timestamp = db.Column(db.DateTime)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<%r %s>' % (self.task_id, self.correct)

两个表,第一个存储用户数据,第二个存储用户数据,存储用户回答问题的数据。

我想根据用户的成功率对用户进行排序,所以我会做这样的事情:

User.query.order_by(User.percentage).all()

但它不会奏效。我究竟做错了什么?甚至可以根据类似的方法使用order_by吗?

2 个答案:

答案 0 :(得分:1)

即使使用SqlAlchemy,您也必须考虑对象集及其值。您想要的查询涉及三个不同的集合:用户,他们的正确答案及其总答案。

想要你想要的是一个类似的查询(警告,这只是一个样本,你可以写得更好)

select userid, cor_count/ans_count from users
inner join (select userid, count(*) cor_count from answers where correct=true group by userid) as correct_answers on users.userid=correct_answers.userid
inner join (select userid, count(*) as ans_count from answers group by userid) as total_answers on total_answers.userid=users.userid
where users.userid='xxxx'
order by 2

所以,你必须在SqlAlchemy中以某种方式表达它。一个指导方针会产生这样的效果:

ans_q = session.query(Task.user_id, func.count(task.id).label('cnt')).group_by(Task.user_id)
corr_ans_q = session.query(Task.user_id, func.count(task.id).label('cnt')).filter(Task.correct).group_by(Task.user_id)

ans_q = alias(ans_q.selectable)
corr_ans_q = alias(corr_ans_q.selectable)

q = session.query(User).join(ans_q,ans_q.c.user_id==User.id).join(corr_ans_q.c.user_id==User.id).order_by(corr_ans_q.c.cnt/ans_q.c.cnt)

答案 1 :(得分:0)

我认为“混合属性”可以解决您的问题。

http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html#module-sqlalchemy.ext.hybrid

UPD:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey

Base = declarative_base()


class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    nickname = Column(String(64), nullable=False, unique=True)
    tasks = relationship('Task', backref='author', lazy='dynamic')

    def number_of_tries(self):
        return self.tasks.count()

    def correct_answers(self):
        return self.tasks.filter_by(correct=True).count()

    def percentage(self):
        try:
            return '{:.2%}'\
                .format(float(self.correct_answers())/self.number_of_tries())
        except ZeroDivisionError:
            return None

    def __repr__(self):
        return '<User %r>' % (self.nickname)


class Task(Base):
    __tablename__ = 'task'

    id = Column(Integer, primary_key=True)
    task_id = Column(Integer)
    correct = Column(Boolean)
    timestamp = Column(DateTime)
    user_id = Column(Integer, ForeignKey('user.id'))

    def __repr__(self):
        return '<%r %s>' % (self.task_id, self.correct)


from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:')
Base.metadata.create_all(bind=engine)

from sqlalchemy.orm import sessionmaker
# create a configured "Session" class
Session = sessionmaker(bind=engine)

# create a Session
session = Session()

from datetime import datetime
task1 = Task(task_id=1, correct=True, timestamp=datetime.now())
task2 = Task(task_id=2, correct=False, timestamp=datetime.now())
task3 = Task(task_id=3, correct=False, timestamp=datetime.now())
task4 = Task(task_id=4, correct=False, timestamp=datetime.now())
task5 = Task(task_id=5, correct=True, timestamp=datetime.now())
task6 = Task(task_id=6, correct=True, timestamp=datetime.now())
user1 = User(nickname="Lekha", tasks=[task1, task2, task3])
user2 = User(nickname="Vanya", tasks=[task4, task5, task6])
session.add(user1, user2)
session.commit()

print(user1.number_of_tries())
print(user1.correct_answers())
print(user1.percentage())