[英]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.