我有一个声明像这样的关系的数据结构(伪代码):
class User:
...
class Rating:
rater = User
post = Post
class Post:
ratings = hasmany(Rating)
page_id = ...
我正在使用这些模型建立一个网站,而且我很懒,所以我将模板传递给当前登录的用户,以及当前页面上的一堆帖子。我的页面需要知道登录用户给每个帖子的评分,所以我使用SQLAlchemy的每个会话一个实例的功能:
posts = session.query(Post).filter(Post.page_id==current_pageid)
ratings = session.query(Post, Rating)\
.filter(Rating.rater==user.id)\
.filter(Post.page_id==current_pageid)
for post in posts:
post.user_rating = None # default value
for post, rating in ratings:
post.user_rating = rating
然后我将模板传递给帖子列表。难道这是一种难看的可怕做法吗?我能以更好的方式做到吗?
答案 0 :(得分:1)
除了您的查询在WHERE
和Post
之间缺少Rating
条款外,您正在做的事情已经足够了:
# ...
.filter(Post.id==Rating.post_id)\
但您也可以在一个查询中获得结果:
qry = (session.query(Post, Rating).
outerjoin(Rating, and_(Post.id==Rating.post_id, Rating.user_id==user.id)).
filter(Post.page_id==current_pageid)
)
res = qry.all() # you can return *res* already to a view, but to get to your results, do below as well:
for post, rating in res:
post.user_rating = rating
posts = [post for post, rating in res]
return posts
请注意,在您的情况下,posts
实际上不是列表,而是查询,如果您第二次迭代它,则可能会丢失user_rating
属性。您应该谨慎地返回会话绑定对象,例如查询到视图。返回上面相同代码中的列表更安全。要修复代码,只需在查询中添加.all()
:
posts = session.query(Post).filter(Post.page_id==current_pageid).all()
答案 1 :(得分:1)
是的,这是不好的做法。它甚至可能(在理论上)在某个时刻击败你,例如当您从同一会话中查询而不清除某些帖子时,SQLAlchemy将返回与当前上下文无关的某些用户已填写评级的相同缓存对象。在实践中,它在大多数情况下都可以找到。
为什么不将(post, rating)
对列表传递给模板?大多数可用于Python的现代模板引擎都可以遍历对列表。
顺便说一下,您可以使用单个查询获取帖子和评分(如果缺少,则评分对象将为None
OUTER JOIN):
session.query(Post, Rating).select_from(Post)\
.outerjoin(Rating, (Post.id==Rating.post_id) & (Rating.rater==…))\
.filter(Post.page_id==…)