Flask首页结合搜索与排序算法时出现属性错误的问题排查
Flask搜索结合Reddit热门排序多帖返回时的AttributeError问题
我在Flask中编写了如下index视图函数,实现简单搜索与Reddit热门排序算法的结合:
@views.route("/index") @login_required def index(): search = request.args.get("search") if search: posts = Post.query.filter(Post.title.contains( search) | Post.text.contains(search)) else: posts = Post.query.all() for post in posts: date = post.date_created ups = len(post.likes) downs = len(post.dislikes) post.hot_score = hot(ups, downs, date) #print(post) #print(post.hot_score) sorted_posts = sorted(posts, key=lambda post: post.hot_score, reverse=True) return render_template("index.html", user=current_user, posts=sorted_posts)
单独使用搜索或排序功能都正常,当搜索仅返回单篇帖子时二者协同工作也正常,但搜索返回多篇帖子时会触发错误:AttributeError: 'Post' object has no attribute 'hot_score'。我尝试注释sorted_posts相关代码,直接返回posts=posts,此时多帖子搜索能正常运行,且打印显示每个Post对象都带有hot_score属性。参考用的Post模型代码如下:
class Post(db.Model): id = db.Column(db.Integer, primary_key=True) title = db.Column(db.String(60), nullable=False) text = db.Column(db.Text, nullable=False) date_created = db.Column(db.DateTime(timezone=True), default=func.now()) author_id = db.Column(db.Integer, db.ForeignKey( "user.id", ondelete="CASCADE"), nullable=False) comments = db.relationship("Comment", backref="post", passive_deletes=True) likes = db.relationship('Like', backref='post', passive_deletes=True) dislikes = db.relationship('Dislike', backref='post', passive_deletes=True)
问题原因
问题出在Flask-SQLAlchemy的惰性查询特性上:当搜索时,Post.query.filter(...)返回的是一个Query对象,而非直接的列表。第一次遍历posts时,ORM会执行查询并加载Post对象,此时给每个对象添加了hot_score属性。但sorted()函数会再次遍历这个Query对象,ORM会重新执行数据库查询,生成一批全新的Post对象——这些新对象并没有被添加过hot_score属性,因此触发了属性不存在的错误。而单帖返回时,Query对象可能只加载一次,后续遍历复用了同一个对象,所以没出现问题。
解决方案
把查询结果转换成列表,确保后续所有操作都针对同一批对象:
方法一:在搜索分支直接转成列表
修改视图函数中的搜索逻辑:
if search: # 把Query对象转为列表,避免后续重复查询生成新对象 posts = list(Post.query.filter(Post.title.contains(search) | Post.text.contains(search))) else: posts = Post.query.all()
方法二:遍历前统一转成列表
不管是搜索还是默认情况,都先把结果转为列表:
# 统一转换为列表 posts = list(posts) for post in posts: date = post.date_created ups = len(post.likes) downs = len(post.dislikes) post.hot_score = hot(ups, downs, date)
这样后续排序操作的就是已经添加了hot_score属性的同一批对象,就不会再出现AttributeError了。
内容的提问来源于stack exchange,提问作者Mattia Beccari
相关产品推荐
相关产品推荐

