ActiveRecord .exists?() 和 default_scope 怪异

ActiveRecord .exists?() and default_scope weirdness

我们的用户 class 上有一个 default_scope,它将用户限制为一组公司:

class User < ActiveRecord::Base
  default_scope do
    c_ids = Authorization.current_company_ids
    includes(:companies).where(companies: { id: c_ids })
  end

  has_many :company_users
  has_many :companies, through: company_users
end

class CompanyUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :company
  validates_uniqueness_of :company_id, scope: :user_id
end

class Company < ActiveRecord::Base
  has_many :company_users
  has_many :users, through: company_users
end

调用 User.lastUser.find_by_email('mhayes@widgetworks.com')User.find(55557) 所有工作和范围都符合预期。

调用 User.exists?(id) 会抛出一个奇怪的错误:

Mysql2::Error: Unknown column 'companies.id' in 'where clause': SELECT  1 AS one FROM `users`  WHERE `companies`.`id` IN (4) AND `users`.`id` = 55557 LIMIT 1

基本上,如果我得到这个,它是说 companies 不是 User 上的列,它是。如果我什至将 sql 复制到 where 语句中,它也会正确计算。

User.where("SELECT  1 AS one FROM `users`  WHERE `companies`.`id` IN (4) AND `users`.`id` = 66668 LIMIT 1")

这让我觉得 default_scope 有一个评估顺序,并且 exists?default_scope.

之前以某种方式被调用

如果我打电话:

User.includes(:companies).where(companies: { id: [4] }).exists?(55557)

有效。这就是 default_scope 正在做的事情,所以我知道 default_scope 范围没有失败。

试试这个

User.exists?(id: 55557)

老实说我不知道​​,但在我看来 exists? 直接建立了 关系 并决定放弃 includes 子句,因为exists? 决定不需要加载其他对象。这发生在 this method call。而 chained exists? 已经通过构建较早的 relation 正确计算了它(其中 includes 被转换为joins).

也许不是一个错误,但可能是另一个要添加到列表中的怪异现象 default_scope

可能最好的解决方案是将 default_scope 中的 includes 变为实际的 joins,并在您实际需要完整记录时手动调用 includes。 (因为 exists? 调用实际上不需要它们)

另一种可能性是在 default_scope 中链接 includesjoins。尽管我很怀疑,但我不知道这对 arel 计算有何影响。 可能有效也可能无效。

或者,如果您真的希望它总体上保持不变,您可以做一些疯狂的事情,例如:

def self.exists?(args)
  self.scoped.exists?(args)
end

这将基本上构建一个具有默认范围的 relation,然后在已构建的 relation 上调用 exists? includes -> joins 魔法。