在使用 SQLAlchemy 的 MapperExtension.create_instance 方法时,我想根据列名提取单独的行数据。我有一个返回大量行的查询,并且发现我们最终会抛弃大部分关联的 ORM 实例;并且构建这些即将被抛弃的实例非常慢。因此,我只想构建我需要的实例!不幸的是,我不能通过简单地限制查询来做到这一点;在我知道是否抛弃它之前,我需要对每一行数据执行相当多的“业务逻辑”处理;我不能在 SQL 中做到这一点。
因此,我想使用 MapperExtension 来处理这个问题:我将继承 MapperExtension,然后覆盖 create_instance;该方法将检查行数据,如果数据值得构建为实例,则返回 EXT_CONTINUE;否则返回其他内容(我尚未决定)。首先,这种方法是否合理?其次,如果它确实有意义,我还没有弄清楚如何找到传递给 create_instance 的参数中所需的数据。我怀疑它在某个地方,但很难找到...我没有得到直接对应于我感兴趣的特定类的行,我得到的是对应于 SQLalchemy 生成的查询的行,碰巧是(例如)表 A、B 和 C 之间的稍微复杂的联接。
问题在于我不知道行的哪些元素对应于 ORM 类中的字段:我想能够选取(例如)A.id、B.weight 和 C.height。我假设在某个地方的映射器、选择上下文或类参数中,某些表列和行偏移量之间存在某种映射。但我还没有找到正确的东西。不过,我已经非常接近了。例如,我发现 selectcontext.statement.columns 包含生成列的名称... 但不包括我感兴趣的表的列名称。例如:
Column(u'A_id', UUID(), ...
...
Column(u'%(32285328 B)s_weight, MSInt(), ...
...
Column(u'%(32285999 C)s_height', MSInt(), ...
因此:如何将 C.height 等列名映射到行的偏移量?
解决方案
row[MyClass.some_element.__clause_element__()]
这将仅使您达到您可以访问外部的类和 aliased() 构造的程度。很有可能这就是您在该问题上所需的一切(即使最终该想法行不通,请继续阅读)。 如果您的语句周围有子查询,从使用 from_self() 或 join() 到多态目标,create_instance() 方法不会向您提供完成此操作所需的转换函数。如果您尝试获取链接到急切加载的行,那么绝对不应该这样做。eagerload() 是关于优化集合的加载。如果您希望查询在两个表之间进行联接,并且您正在寻找要联接的表的筛选,请使用 join()。 但最重要的是,create_instance() 来自 SQLAlchemy 的 0.1 版,我怀疑有人会用它来做任何事情,而且它没有说“跳过此行”的能力。它必须返回一些内容,否则映射器将自行创建实例。因此,无论您如何很好地解释行,您想要做的事情都没有挂钩。 如果我真的想做这样的事情,可能会更容易修补返回结果代理的 “fetchall()” 方法以过滤行,并将其发送到 Query.instances()。可以将任何结果发送到此方法。但是,如果查询对映射的可选择元素进行了转换和操作,它也需要原始的 QueryContext 才能知道如何转换。但我也不会费心去做这件事。 总体而言,如果速度对整个过程来说非常重要,以至于创建对象是一个很大的差异,我会使其在整个操作中根本不需要映射对象,或者我会使用缓存,或者从结果集中手动生成我需要的对象。我还会确保我可以在我使用的可选择元素中访问所有目标列,以便我可以从结果行中重新提取,这意味着我不会在 ORM 中使用自动子查询/别名生成函数,或者我直接使用表达式语言(如果您真的渴望速度并且有心情编写大段优化的代码,您可能应该只使用表达式语言)。 因此,您在这里必须问的真正问题是:
您是否已验证速度的真正差异是从行创建对象。即不提取行或提取其列等。 该行是否有一些您不需要的昂贵列?您是否研究过 deferred() ? 这些业务规则是什么,为什么不能在 SQL 中完成,作为存储过程等。 您在这里真正跳过的行有多少千行,以“慢”到不“跳过”它们? 您是否研究过让对象已经存在的方法,例如内存缓存、预加载等。对于许多方案,这非常合适。 所有这些都不起作用,您真的想攻克一些自制的优化代码。那么,为什么不直接使用 SQL 表达式语言呢?如果您最终只处理视图层,结果行非常友好(它们允许“属性”样式访问等),或者从中构建一些快速“生成对象”的例程。ORM 呈现了 SQL 表达式语言的一个非常具体的用例,如果您真的需要比它轻量得多的东西,您最好跳过它。