我在Ruby on Rails 4.1.4上有一个项目,使用activeadmin 1.0.0.pre from git://github.com/activeadmin/activeadmin
,pg 0.17.1
,PostgreSQL 9.3
在项目中我有这些模型:
class User
has_one :account
class Account
belongs_to :user
has_many :project_accounts
has_many :projects, :through => :project_accounts
class Project
# the project has a boolean attribute 'archive'
has_many :project_accounts
class ProjectAccount
belongs_to :account
belongs_to :project
我有一项任务是在索引页面上实现一个ActiveAdmin过滤器,名为" by_active_projects",因此它必须显示已定义活动项目数的用户,这意味着此类项目有archive == false
的人。
例如。如果我输入' 2'在过滤器中,它必须找到具有正好2个活动项目的帐户。
现在我已注册"帐号" ActiveAdmin中的资源,admin/account.rb
内已添加filter :by_active_projects_eq
之后,我为帐户模型(models / account.rb)定义了范围having_active_projects
:
scope :having_active_projects, ->(number) { joins(:projects).where("projects.archive = ?", false).having("count(projects) = ?", number).group("accounts.id") }
下一步,我已经为此帐户模型定义了一个搜索者:
ransacker :by_active_projects, formatter: proc{ |v|
data = Account.having_active_projects(v).map(&:id)
data ||= nil
} do |parent|
parent.table[:id]
end
在开发数据库中有一个帐户,它有8个活动项目,过滤效果很好。但是,当我试图通过2个活动项目过滤帐户时,我遇到了错误。在DB中有三个这样的帐户,错误页面报告我查询语法错误:
SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' LIMIT 30 OFFSET 0) subquery_for_count
如您所见,而不是
"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')
正在生成这个东西:
"accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114'
我试图深入研究源代码,在ActiveRecord,ActiveAdmin和Ransack lib中的代码段上移动断点,并发现在 <的帮助下构建关系EM>阿雷尔::节点::平等 即可。我不确定这是原因,但我可以肯定地说:
LIB / active_record /关系/ query_methods.rb `
560 def where!(opts = :chain, *rest)
561 if opts == :chain
562 WhereChain.new(self)
563 else
564 references!(PredicateBuilder.references(opts)) if Hash === opts
565 self.where_values += build_where(opts, rest)
566 self
567 end
568 end`
self
这是帐户的活动记录关系;
在第565行调用build_where
之前,self.to_sql
等于
SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL ORDER BY "accounts"."created_at" desc
调用它并将结果分配给self.where_values
,
self.to_sql
等于
SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114' ORDER BY "accounts"."created_at" desc
有关此事的任何帮助或信息表示赞赏!谢谢!
答案 0 :(得分:4)
所以我找到了解决方案:
首先,我在<{1}}中更改了我的过滤器
admin/account.rb
到
filter :by_active_projects_eq
这种方法导致了正确的SQL生成,
filter :by_active_projects_in,
:as => :string
之后我还必须从
改变我的搜索者"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')
到
ransacker :by_active_projects, formatter: proc{ |v|
data = Account.having_active_projects(v).map(&:id)
data ||= nil
} do |parent|
parent.table[:id]
end
因为它的实现方式也导致了不正确的查询:例如,没有这样的帐户只有5个活动项目。在那种情况下
ransacker :by_active_projects, formatter: proc{ |v|
data = Account.having_active_projects(v).pluck(:id)
data.present? ? data : nil
} do |parent|
parent.table[:id]
end
返回“Empty Array”,处理这个带有data = Account.having_active_projects(v).pluck(:id)
的数组从未实际返回data ||= nil
,导致SQL如下:
nil
注意导致问题的SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."id" IN () LIMIT 30 OFFSET 0) subquery_for_count
部分。
将"accounts"."id" IN ()
替换为data ||= nil
后,如果data.present? ? data : nil
不存在,则会为其分配data
,并且SQL中的该部分是核心生成的:nil