我目前正在开发一个模型,我将判断文章的相关性。这遵循Hacker News的算法。这是我在app/articles/models.py
from app.extensions import db
class Article(db.Model):
""" database representation of an article """
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(128))
subtitle = db.Column(db.String(512))
body = db.Column(db.Text())
votes = db.Column(db.Integer, default=1)
views = db.Column(db.Integer, default=1)
timestamp = db.Column(db.DateTime, default=datetime.utcnow)
def popularity(self, gravity=1.8):
""" uses hacker news popularity rating """
submit_delta = (self.timestamp - datetime.utcnow()).total_seconds()
time_decay = submit_delta / 60 / 60
popularity = (self.views - 1) / (time_decay + 2) ** gravity
return popularity
目前,我正在尝试按popularity
的结果排序。
>>> from app.articles.models import Article
>>> Article.query.order_by(Article.popularity()).all()
这不起作用。我如何按他们的受欢迎程度对文章进行排序?
答案 0 :(得分:3)
您可以使用hybrid methods创建一个方法,在类调用时生成SQL表达式(用于查询),但在实例调用时表现得像常规方法。
这是一个有效的例子。它打印由python和数据库计算的流行度。由于时间和四舍五入,这些会略有不同。
from datetime import datetime
from sqlalchemy import create_engine, Integer, Column, DateTime, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_method
from sqlalchemy.orm import Session
engine = create_engine('postgresql:///example', echo=True)
Base = declarative_base(bind=engine)
session = Session(bind=engine)
class Article(Base):
__tablename__ = 'article'
id = Column(Integer, primary_key=True)
views = Column(Integer, nullable=False, default=1)
ts = Column(DateTime, nullable=False, default=datetime.utcnow)
@hybrid_method
def popularity(self, gravity=1.8):
seconds = (self.ts - datetime.utcnow()).total_seconds()
hours = seconds / 3600
return (self.views - 1) / (hours + 2) ** gravity
@popularity.expression
def popularity(self, gravity=1.8):
seconds = func.extract('epoch', self.ts - func.now())
hours = seconds / 3600
return (self.views - 1) / func.power((hours + 2), gravity)
Base.metadata.create_all()
a1 = Article(views=100)
a2 = Article(views=200)
session.add_all((a1, a2))
session.commit()
comparison = session.query(Article, Article.popularity()).all()
for a, pop in comparison:
print 'py: {} db: {}'.format(a.popularity(), pop)
这适用于PostgreSQL,但func.power
和func.extract
在其他数据库中的工作方式可能不同。 SQLite特别没有power
,extract
的实现方式不同。
答案 1 :(得分:1)
如果您希望将其用作"命令,那么您需要将流行度计算重写为sql表达式。在数据库中。您的其他选择是获取所有文章并在python中排序(不适用于较大的数据集)或预先计算所有流行度值并将其缓存在数据库的数字字段中,并对其进行排序。
例如(这是Postgres特有的,我没有使用Flask成语,但你应该明白这一点):
order_exp = "(article.views - 1) / power(2 + extract(epoch from (now() at time zone 'UTC' - timestamp))/3600, :gravity)"
order = sqlalchemy.text(order_exp).bindparams(gravity=1.8)
print(session.query(Article).order_by(order).all())