sqlalchemy:滥用临时场地

时间:2011-11-28 07:00:15

标签: python sqlalchemy

我有一个声明像这样的关系的数据结构(伪代码):

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

然后我将模板传递给帖子列表。难道这是一种难看的可怕做法吗?我能以更好的方式做到吗?

2 个答案:

答案 0 :(得分:1)

除了您的查询在WHEREPost之间缺少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==…)