【问题标题】:In SQLAlchemy, why is my load_only not filtering any columns that I have specified?在 SQLAlchemy 中,为什么我的 load_only 不过滤我指定的任何列?
【发布时间】:2018-10-23 09:07:22
【问题描述】:

基本上我已经创建了一个数据库,在正常的完整查询中,这是我使用的代码和生成的响应。

db.session.query(User).all()

生成的查询如下:

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>,  <User(email='emailhoward', fullname='None', company='None', address='None', password='passwordhoward')>

这是合乎逻辑的,因为我正在从表中提取所有内容。但是,当我尝试使用 load_only 专门选择一列时,在本例中为电子邮件列。我使用的代码是:

db.session.query(User).options(load_only(User.address)).all()
db.session.query(User).options(load_only('email')).all()

两个命令都给我相同的结果:

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>,<User(email='emailhoward', fullname='None', company='None', address='None', password='passwordhoward')>

这非常奇怪,因为我应该在查询中只得到一列。但是,当我使用这个时:

db.session.query(User.email).select_from(User).filter_by(email=email).first()[0]

它神奇地只为我返回一列。我需要使用 load_only,因为我有想要重用相同函数的动态表,而不是维护许多函数集。谁能告诉 load_only 命令有什么问题,或者我做错了什么?

谢谢。

【问题讨论】:

  • 查看您的日志。仅加载完全符合您的预期,但您的 __str____repr__ 正在访问其他列,因此您应该看到一堆额外的 SELECT 语句,它们根据需要获取延迟属性的值。延迟属性不包含 None (或其他一些“无价值”值)。它们在访问时被延迟和加载。

标签: python sql python-3.x sqlalchemy


【解决方案1】:

没有问题,只是有点误会;

<User(email='howard@howard.com', fullname='Howard', company='howard', address='None', password='howard')>

User 模型对象的string representation,它是User.__repr__() 方法,它在访问延迟列时拉入它们。

使用load_only(),您可以定义一组列以最初为实体加载,同时推迟所有其他列。但是推迟一列并不意味着它在某种程度上无法使用或包含一些“无价值”标记(实际上它确实如此,在引擎盖下)。当第一次引用每个延迟属性时,SQLAlchemy 将发出一个 SELECT 以获取其值。这从日志中应该很明显:

In [7]: u = session.query(User).options(load_only(User.email)).first()
2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine SELECT user.id AS user_id, user.email AS user_email 
FROM user
 LIMIT ? OFFSET ?
2018-05-14 16:04:49,218 INFO sqlalchemy.engine.base.Engine (1, 0)

In [8]: u.fullname
2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine SELECT user.fullname AS user_fullname 
FROM user 
WHERE user.id = ?
2018-05-14 16:04:53,773 INFO sqlalchemy.engine.base.Engine (2,)
Out[8]: 'Bar'

您可以使用检查 API 检查列是否已被延迟。 InstanceState.unloaded 持有一组没有加载值的键。使用它,您可以将 User.__repr__ 修改为:

class User(Base):
    ...

    def __repr__(self):
        state = inspect(self)
        def ga(attr):
            return (repr(getattr(self, attr))
                    if attr not in state.unloaded
                    else "<deferred>")

        attrs = " ".join([f"{attr.key}={ga(attr.key)}"
                          for attr in state.attrs])
        return f"<User {attrs}>"

或者,您可以遍历显示AttributeState.loaded_valueInstanceState.attrs,如果尚未加载值,则计算结果为符号NO_VALUE

class User(Base):
    ...

    def __repr__(self):
        state = inspect(self)    
        attrs = " ".join([f"{attr.key}={attr.loaded_value!r}"
                          for attr in state.attrs])
        return f"<User {attrs}>"

【讨论】:

  • 非常感谢!没想到从这方面去查。我从 MySQLdb 切换到 sqlalchemy,被学习曲线震惊了。
  • 这信息量很大,从一个答案跳到另一个答案。非常感谢您的解释,从文档中不是很清楚。感觉 load_only 执行谓词下推,显然它确实执行了,但在访问其他列时执行自动选择查询。
猜你喜欢
  • 2023-04-05
  • 2018-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-31
  • 1970-01-01
相关资源
最近更新 更多