Flask-SQLAlchemy 中的 SQLAlchemy 2.0 版本 User.query.get(1)?

问题描述 投票:0回答:4

问题

Query.get()
方法在 SQLAlchemy 2.0 中已被弃用。因此,Flask-SQLAlchemy 查询接口被视为遗留。因此,在我的 Flask-SQLAlchemy 项目中运行
User.query.get(1)
会出现如下所示的遗留警告:

>>> User.query.get(1)
<stdin>:1: LegacyAPIWarning: The Query.get() method 
is considered legacy as of the 1.x series of SQLAlchemy 
and becomes a legacy construct in 2.0. The method is 
now available as Session.get() (deprecated since: 2.0) 
(Background on SQLAlchemy 2.0 at: https://sqlalche.me/e/b8d9)
<User spongebob>

我的问题

Flask-SQLAlchemy 中新的、与 SQLAlchemy 2.0 兼容的

User.query.get(1)
版本是什么?更具体地说,为什么 Flask-SQLAlchemy 文档推荐下面的方法 #2,尽管根据我对 SQLAlchemy 2.0 迁移指南的阅读,方法 #1 似乎是新版本?

方法#1:

db.session.get(User, 1)

第一种方法来自 SQLAlchemy 文档,特别是 SQLAlchemy 2.0 迁移 - ORM 使用 指南。将该指南中的“2.0 样式”示例转换为我的 Flask-SQLAlchemy 项目会生成以下代码,该代码运行良好:

>>> db.session.get(User, 1)
<User spongebob>

据我所知,Flask-SQLAlchemy 3.0.x 文档中没有提到这种使用

session.get()
的方法,除了关于
get_or_404
的 API 参考部分中的简要介绍。

方法#2:

db.session.execute(db.select(User).filter_by(id=1)).scalar()

此方法来自 Flask-SQLAlchemy 文档,该文档建议使用

session.execute(select(...))
作为旧版
Model.query
session.query
的替代品。这也很好用:

>>> db.session.execute(db.select(User).filter_by(id=1)).scalar()
<User spongebob>

方法 #1 与方法 #2 与传统方法

方法 #1 (

db.session.get(User, 1)
) 似乎与旧方法 (
User.query.get(1)
) 最相似,因为它在第一次运行时将结果缓存在
session
中,并且不会对数据库发出不必要的额外调用。这可以在打开回显的 REPL 中看到,即
db.engine.echo = True
。相比之下,方法 #2 (
session.execute(select(...))
) 每次都会按预期访问数据库。

我的设置/环境

  1. 版本:Python 3.11 虚拟环境中的 Flask 2.2.2、Flask-SQLAlchemy 3.0.3 和 SQLAlchemy 2.0.1。

  2. 我正在使用 Flask Mega-Tutorial 中定义的项目结构,特别是第四部分数据库

flask sqlalchemy flask-sqlalchemy
4个回答
10
投票

Model.query.get(id)
替换所有
db.session.get(Model, id)
既可怕又不优雅:明显丧失了可读性。

我们需要更好的解决方案!


5
投票

根据https://docs.sqlalchemy.org/en/14/changelog/migration_20.html#orm-query-get-method-moves-to-session

Query.get() 方法保留用于遗留目的,但主要接口现在是 Session.get() 方法:

# legacy usage
user_obj = session.query(User).get(5)

迁移到2.0

在1.4 / 2.0中,Session对象添加了新的Session.get()方法:

# 1.4 / 2.0 cross-compatible use
user_obj = session.get(User, 5)

与flask_sqlalchemy兼容,这应该可以很好地工作

user_obj = db.session.get(User, 5)

2
投票

我找到了一种方法,可以避免使用已弃用的方法,同时仍然允许您调用模型上的方法,而不必访问

db.session

首先我注意到您可以从模型访问会话:

>>> MyModel.query.session
<sqlalchemy.orm.session.Session object at 0x7f1d9fe86c10>

所以你可以使用以下详细代码:

>>> MyModel.query.session.get(MyModel, value)
<MyModel 1>

但这并不是特别优雅。相反,就我而言,我已经让所有模型继承自一个名为

IDMixin
:

的自定义类
from flask_sqlalchemy import SQLAlchemy


db = SqlAlchemy()


class IDMixin:
    id = db.Column(db.Integer, primary_key=True)


class MyModel(IDMixin, db.Model):
    field = db.column(db.String(50))

所以我向该 mixin 添加了一个类方法:

class IDMixin:
    id = db.Column(db.Integer, primary_key=True)

    @classmethod
    def get_by_id(cls, id_):
        return cls.query.session.get(cls, id_)

现在我可以按如下方式检索单个对象:

>>> MyModel.get_by_id(1)
<MyModel 1>

它需要一些自定义代码,但我对结果很满意。


0
投票

使用带有用户注册的 Flask 应用程序时,我遇到了这个问题。 Flask登录文档有一个有小错误的示例:

@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)
                ^^^ not correct

所以,我将其更改为:

@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
                ^^^^^^^^^ deprecated

最后,我使用了方法#1,一切都很顺利:

@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, int(user_id))
© www.soinside.com 2019 - 2024. All rights reserved.