为啥 SQLAlchemy 在查询中标记列
Posted
技术标签:
【中文标题】为啥 SQLAlchemy 在查询中标记列【英文标题】:Why Does SQLAlchemy Label Columns in Query为什么 SQLAlchemy 在查询中标记列 【发布时间】:2019-04-18 22:00:48 【问题描述】:当我在 SQLAlchemy 中进行查询时,我注意到查询对每一列都使用了 AS 关键字。它为每一列设置alias_name
= column_name
。
例如,如果我运行命令print(session.query(DefaultLog))
,它会返回:
注意:DefaultLog
是我的表格对象。
SELECT default_log.id AS default_log_id, default_log.msg AS default_log_msg, default_log.logger_time AS default_log_logger_time, default_log.logger_line AS default_log_logger_line, default_log.logger_filepath AS default_log_logger_filepath, default_log.level AS default_log_level, default_log.logger_name AS default_log_logger_name, default_log.logger_method AS default_log_logger_method, default_log.hostname AS default_log_hostname
FROM default_log
为什么它使用别名 = 原始名称?有什么方法可以禁用此行为?
提前谢谢你!
【问题讨论】:
为什么要禁用它? 为什么您认为需要禁用列别名?您正在使用 ORM 运行查询,然后 ORM 使用结果来构建 Python 对象。别名用于标记结果集中的每一列,使从结果集到 Python 对象的自动映射易于管理,而且您无需读取该结果集,ORM 是。 @MartijnPieters @Ilja Everilä - 我问是因为我认为我做错了什么,或者额外的AS
会减慢速度。从答案中,我现在看到这是预期的行为并且是可取的。
【参考方案1】:
Query.statement
:
此查询表示的完整 SELECT 语句。
默认情况下,该语句不会应用消除歧义的标签 除非首先调用 with_labels(True) ,否则构造。
使用这个模型:
class DefaultLog(Base):
id = sa.Column(sa.Integer, primary_key=True)
msg = sa.Column(sa.String(128))
logger_time = sa.Column(sa.DateTime)
logger_line = sa.Column(sa.Integer)
print(session.query(DefaultLog).statement)
显示:
SELECT defaultlog.id, defaultlog.msg, defaultlog.logger_time, defaultlog.logger_line
FROM defaultlog
print(session.query(DefaultLog).with_labels().statement)
显示:
SELECT defaultlog.id AS defaultlog_id, defaultlog.msg AS defaultlog_msg, defaultlog.logger_time AS defaultlog_logger_time, defaultlog.logger_line AS defaultlog_logger_line
FROM defaultlog
你问:
为什么它使用别名 = 原始名称?
来自Query.with_labels
文档:
...这通常用于区分多个同名表中的列。
因此,如果您想发出调用多个表的单个查询,没有什么可以阻止那些具有共享相同名称的列的表。
有什么方法可以禁用此行为吗?
同样来自Query.with_labels
文档:
当查询实际发出 SQL 来加载行时,它总是使用列 标记。
所有检索行的方法(get()
、one()
、one_or_none()
、all()
和迭代 Query
)都通过 Query.__iter__()
方法路由:
def __iter__(self):
context = self._compile_context()
context.statement.use_labels = True
if self._autoflush and not self._populate_existing:
self.session._autoflush()
return self._execute_and_instances(context)
... 此行对标签用法进行硬编码:context.statement.use_labels = True
。所以它是“烘焙”的,不能被禁用。
你可以执行不带标签的语句:
session.execute(session.query(DefaultLog).statement)
...但这将 ORM 排除在外。
【讨论】:
感谢您的明确回答@SuperShoot!这肯定回答了我的问题,我现在明白为什么 SQLAlchemy 会做出这个决定。谢谢!【参考方案2】:可以破解 sqlachemy 查询类以不添加标签。但是必须知道,当一个表在查询中使用两次时,这将中断。例如,自连接或连接到另一个表。
from sqlalchemy.orm import Query
class MyQuery(Query):
def __iter__(self):
"""Patch to disable auto labels"""
context = self._compile_context(labels=False)
context.statement.use_labels = False
if self._autoflush and not self._populate_existing:
self.session._autoflush()
return self._execute_and_instances(context)
然后使用according to mtth answer
sessionmaker(bind=engine, query_cls=MyQuery)
【讨论】:
以上是关于为啥 SQLAlchemy 在查询中标记列的主要内容,如果未能解决你的问题,请参考以下文章