我在 Ruby on Rails 4.1.4 上有一个项目,使用来自 git://github.com/activeadmin/activeadmin 的 activeadmin 1.0.0.pre
,pg 0.17.1
,PostgreSQL 9.3
在项目中我有这些模型:
类用户 has_one:账户
类账户 属于:用户 有很多:project_accounts has_many :项目, :through => :project_accounts
类项目 # 该项目有一个 bool 属性 'archive' has_many :project_accounts
类 ProjectAccount 属于:帐户 属于:项目
我有一个任务是在索引页面上实现一个名为“by_active_projects”的 ActiveAdmin 过滤器,因此它必须向用户显示已定义数量的事件项目,这意味着此类项目具有 archive = =假
。
例如。如果我在过滤器中输入“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 个事件项目过滤帐户时,我遇到了错误。在数据库中有三个这样的帐户,错误页面报告我查询语法错误:
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 库中的代码片段上移动断点,并发现关系是在 Arel 的帮助下构建的: :Nodes::Equality。我不确定这是不是原因,但我可以肯定地说:
lib/active_record/relation/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
这里是 Account 的 Active Record 关系;
在第 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
如有任何帮助或信息,我们将不胜感激!谢谢!
最佳答案
所以我找到了解决方案:
首先,我更改了 admin/account.rb
中的过滤器
filter :by_active_projects_eq
到
filter :by_active_projects_in,
:as => :string
这种方法导致了正确的 SQL 生成,
"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
到
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
因为它的实现方式也导致了不正确的查询:例如,没有这样的帐户恰好有 5 个事件项目。在那种情况下
data = Account.having_active_projects(v).pluck(:id)
返回“空数组”,并使用data ||= nil
处理此数组实际上从未返回nil
,这导致 SQL 如下所示:
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.present 替换
,如果 data ||= nil
之后? ? data : nildata
不存在,它会被分配一个 nil
,SQL 中的那部分是正确生成的:“accounts”。 id"IN (NULL)
关于ruby - Rails+ActiveAdmin - 使用 ransacker 过滤会抛出错误 PG::SyntaxError: ERROR: syntax error at or near ",",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27094572/