带命名空间模型的 ActiveRecord WHERE
ActiveRecord WHERE with namespaced models
我有两个模型 相同 namespace/module:
module ReverseAuction
class Demand < ApplicationRecord
belongs_to :purchase_order, inverse_of: :demands, counter_cache: true
end
end
module ReverseAuction
class PurchaseOrder < ApplicationRecord
has_many :demands
end
end
请注意,我不必为模型指定 class_name,因为它们在同一个模块中,并且这种关系运作良好。
当我尝试使用关系本身的名称查询 includes 时,它工作正常,例如:
ReverseAuction::PurchaseOrder.all.includes(:demands) # all right .. AR is able to figure out that *:demands* correspond to the 'reverse_auction_demands' table
但是当我尝试在此查询中使用 where 时,AR 似乎无法自行找出(命名空间的)table 名称,因此:
ReverseAuction::PurchaseOrder.includes(:demands).where(demands: {user_id: 1}) # gives me error: 'ERROR: missing FROM-clause entry for table "demands"'
但是如果我指定完整的解析(命名空间)模型名称,那么 where 会很顺利:
ReverseAuction::PurchaseOrder.includes(:demands).where(reverse_auction_demands: {user_id: 1}) # works pretty well
AR 可以 从 includes 中的关系推断 table 命名空间模型的名称是正常的,但是 不能在哪里,或者我错过了重点?
Is that normal that AR can infere table name of namespaced models from
relations in includes but can't in where?
是的。这是一个leaky abstraction的例子。
关联是围绕 SQL 连接的面向对象的抽象,让您可以做一些有趣的事情,而 AR 则担心编写 SQL 来连接它们并维护记录之间的内存耦合。 .joins
、.left_joins
.includes
和 .eager_load
都“知道”您的关联并进行抽象。因为你有这个面向对象的抽象 .includes
足够聪明,可以弄清楚模块嵌套在编写连接时应该如何影响 class 名称和 table 名称。
.where
和 ActiveRecord query interface 的所有其他部分都不那么聪明。这只是一个以编程方式生成 SQL 查询的 API。
当您执行 .where(foo: 'bar')
时,它足够聪明地将其转换为 WHERE table_name.foo = 'bar'
,因为 class 知道它自己的 table 名称。
当您执行 .where(demands: {user_id: 1})
时,该方法实际上并不知道您的关联、其他模型 classes 或模式,只是生成 WHERE demands.user_id = 1
因为这就是它转换嵌套哈希的方式进入 SQL.
请注意,这实际上与命名空间无关。当你这样做时:
.where(reverse_auction_demands: {user_id: 1})
之所以有效,是因为您使用了正确的 table 名称。如果您使用的 non-conventional table 名称与模型不一致,您将遇到完全相同的问题。
如果您想基于 class 创建一个 where 子句而不对 table 名称进行硬编码,请将范围传递给 where:
.where(
ReverseAuction::Demand.where(user_id: 1)
)
或使用 Arel:
.where(
ReverseAuction::Demand.arel_table[:user_id].eq(1)
)
我有两个模型 相同 namespace/module:
module ReverseAuction
class Demand < ApplicationRecord
belongs_to :purchase_order, inverse_of: :demands, counter_cache: true
end
end
module ReverseAuction
class PurchaseOrder < ApplicationRecord
has_many :demands
end
end
请注意,我不必为模型指定 class_name,因为它们在同一个模块中,并且这种关系运作良好。
当我尝试使用关系本身的名称查询 includes 时,它工作正常,例如:
ReverseAuction::PurchaseOrder.all.includes(:demands) # all right .. AR is able to figure out that *:demands* correspond to the 'reverse_auction_demands' table
但是当我尝试在此查询中使用 where 时,AR 似乎无法自行找出(命名空间的)table 名称,因此:
ReverseAuction::PurchaseOrder.includes(:demands).where(demands: {user_id: 1}) # gives me error: 'ERROR: missing FROM-clause entry for table "demands"'
但是如果我指定完整的解析(命名空间)模型名称,那么 where 会很顺利:
ReverseAuction::PurchaseOrder.includes(:demands).where(reverse_auction_demands: {user_id: 1}) # works pretty well
AR 可以 从 includes 中的关系推断 table 命名空间模型的名称是正常的,但是 不能在哪里,或者我错过了重点?
Is that normal that AR can infere table name of namespaced models from relations in includes but can't in where?
是的。这是一个leaky abstraction的例子。
关联是围绕 SQL 连接的面向对象的抽象,让您可以做一些有趣的事情,而 AR 则担心编写 SQL 来连接它们并维护记录之间的内存耦合。 .joins
、.left_joins
.includes
和 .eager_load
都“知道”您的关联并进行抽象。因为你有这个面向对象的抽象 .includes
足够聪明,可以弄清楚模块嵌套在编写连接时应该如何影响 class 名称和 table 名称。
.where
和 ActiveRecord query interface 的所有其他部分都不那么聪明。这只是一个以编程方式生成 SQL 查询的 API。
当您执行 .where(foo: 'bar')
时,它足够聪明地将其转换为 WHERE table_name.foo = 'bar'
,因为 class 知道它自己的 table 名称。
当您执行 .where(demands: {user_id: 1})
时,该方法实际上并不知道您的关联、其他模型 classes 或模式,只是生成 WHERE demands.user_id = 1
因为这就是它转换嵌套哈希的方式进入 SQL.
请注意,这实际上与命名空间无关。当你这样做时:
.where(reverse_auction_demands: {user_id: 1})
之所以有效,是因为您使用了正确的 table 名称。如果您使用的 non-conventional table 名称与模型不一致,您将遇到完全相同的问题。
如果您想基于 class 创建一个 where 子句而不对 table 名称进行硬编码,请将范围传递给 where:
.where(
ReverseAuction::Demand.where(user_id: 1)
)
或使用 Arel:
.where(
ReverseAuction::Demand.arel_table[:user_id].eq(1)
)