简体   繁体   English

SQLAlchemy:如何基于计算值使用order_by?

[英]SQLAlchemy: How to use order_by based on a calculated value?

So I have the following database model: 所以我有以下数据库模型:

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)

Two tables, the first one storing User data, the second one in relation with the first, storing data about questions the User answers. 两个表,第一个表存储用户数据,第二个表与第一个表相关,存储有关用户回答的问题的数据。

I would like to sort the users based on their success ratio, so I would do something like this: 我想根据用户的成功率对他们进行排序,所以我会做这样的事情:

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

but it won't work. 但它行不通。 What am I doing wrong? 我究竟做错了什么? Is it even possible to use order_by based on a method like that? 甚至可以基于这样的方法使用order_by吗?

Even with SqlAlchemy, you have to think in sets of objects and their values. 即使使用SqlAlchemy,您也必须考虑对象集及其值。 The query you want involves three different sets: Users, their correct answers and their total answers. 您想要的查询涉及三个不同的集合:用户,正确答案和总答案。

Want you want is a query like that (warning, that's just a sample, you could write it much better) 想要一个这样的查询(警告,这只是一个示例,您可以写得更好)

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

so, you have to formulate this (somehow) in SqlAlchemy. 因此,您必须(以某种方式)在SqlAlchemy中制定公式。 A guideline would by something to that effect: 准则可以达到以下目的:

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)

I think that "Hybrid Attributes" will solve you problem. 我认为“混合属性”将解决您的问题。

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

UPD: 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())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM