SQLAlchemy:如何根据计算值使用 order_by?
SQLAlchemy: How to use order_by based on a calculated value?
所以我有以下数据库模型:
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 吗?
我认为 "Hybrid Attributes" 可以解决您的问题。
http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html#module-sqlalchemy.ext.hybrid
更新:
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())
即使使用 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)
所以我有以下数据库模型:
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 吗?
我认为 "Hybrid Attributes" 可以解决您的问题。
http://docs.sqlalchemy.org/en/latest/orm/extensions/hybrid.html#module-sqlalchemy.ext.hybrid
更新:
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())
即使使用 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)