在sqlalchemy中查询瞬态模型的属性

时间:2018-03-02 03:45:48

标签: flask sqlalchemy flask-sqlalchemy flask-login

我正在使用flask-sqlalchemy和flask-login。

我的烧瓶登录用户加载器目前看起来像这样:

@login_manager.user_loader
def load_user(user_id):
    return User.query.filter_by(id=user_id).options(load_only('id')).first()

这意味着每次用户需要登录load_user() 将运行数据库查询以返回User对象。在我的情况下,我几乎从不需要其他用户字段,如电子邮件,名称等,所以我指定sqlalchemy只加载id字段。这将允许mysql优化器相当快地执行查询。

但是,我甚至不需要执行此查询,因为:

  • 当用户登录并发出会话时,可以保证他有唯一的ID。
  • 使用我的密钥对Flask-login的cookie进行签名,因此随机用户无法欺骗其用户ID以通过更改其ID来冒充其他用户。

此外,由于某些特定的商业原因,每个登录用户必须每1秒发送一次心跳信号。这意味着每秒都会执行此用户ID提取查询,从而在我的数据库服务器上产生相当大的负载(我有大约1000个用户同时执行此操作)。这个要求是不可协商的,我无法通过其他方法提高速度,例如使用内存服务器。

我想这样做:

@login_manager.user_loader
def load_user(user_id):
    return User(id=user_id)

请注意,在这种情况下,将返回具有用户ID的伪对象。

从其他文件,我做

from flask_login import current_user

以上一行为我提供了当前登录用户的句柄。

然而,当我想要获取用户的其他属性时,使用我提出的方法将无法工作,更重要的是它的关系。

这意味着我不能简单地执行current_user.email或:

current_user.friends #this is a relationship

我尝试了这个,它只返回None。 (但是,我可以像通常那样获得用户的身份)

有没有办法实例化一个模型并让sqlalchemy认为我在创建模型时指定的任何属性都是sqlalchemy查询的结果? 从本质上讲,sqlalchemy认为只使用load_only()方法加载了一些属性。

这样,如果我current_user.email,它会在未加载的情况下获取电子邮件。

1 个答案:

答案 0 :(得分:1)

在与Michael Bayer(sqlalchemy的创建者)here讨论此问题之后,这是他建议的解决方案:

使对象像分离和过期一样,然后与会话关联:

from sqlalchemy.orm.session import make_transient_to_detached 

@login_manager.user_loader
def load_user(user_id):
    u = User(id=int(user_id))     # will not do any db query
    make_transient_to_detached(u) # This is the suggested solution
    db.session.add(u)             # add the detached object to session
    return u

稍后,可以这样做:

from flask_login import current_user

并访问current_user的其他属性,如:

print(current_user.email)  # will perform a database query to fetch email