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.last
或 User.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
中链接 includes
和 joins
。尽管我很怀疑,但我不知道这对 arel
计算有何影响。 可能有效也可能无效。
或者,如果您真的希望它总体上保持不变,您可以做一些疯狂的事情,例如:
def self.exists?(args)
self.scoped.exists?(args)
end
这将基本上构建一个具有默认范围的 relation,然后在已构建的 relation 上调用 exists?
includes
-> joins
魔法。
我们的用户 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.last
或 User.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
中链接 includes
和 joins
。尽管我很怀疑,但我不知道这对 arel
计算有何影响。 可能有效也可能无效。
或者,如果您真的希望它总体上保持不变,您可以做一些疯狂的事情,例如:
def self.exists?(args)
self.scoped.exists?(args)
end
这将基本上构建一个具有默认范围的 relation,然后在已构建的 relation 上调用 exists?
includes
-> joins
魔法。