cha*_*ann 6 ruby postgresql activeadmin ransack ruby-on-rails-4
我对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
我有一个任务是在索引页面上实现一个名为"by_active_projects"的ActiveAdmin过滤器,因此它必须显示已定义活动项目数的用户,这意味着这样的项目具有archive == false.例如,如果我在过滤器中键入"2",则必须找到具有正好2个活动项目的此类帐户.
现在我在ActiveAdmin中注册了"帐户"资源,并在admin/account.rb我添加的内部filter :by_active_projects_eq
之后我having_active_projects为Account模型(models/account.rb)定义了一个范围:
scope :having_active_projects, ->(number) { joins(:projects).where("projects.archive = ?", false).having("count(projects) = ?", number).group("accounts.id") }
Run Code Online (Sandbox Code Playgroud)
下一步,我为Account模型定义了一个ransacker,如下所示:
ransacker :by_active_projects, formatter: proc{ |v|
data = Account.having_active_projects(v).map(&:id)
data ||= nil
} do |parent|
parent.table[:id]
end
Run Code Online (Sandbox Code Playgroud)
在开发数据库中有一个帐户,它有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
Run Code Online (Sandbox Code Playgroud)
如你所见,而不是
"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')
Run Code Online (Sandbox Code Playgroud)
这个东西正在生成:
"accounts"."id" = 'e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114'
Run Code Online (Sandbox Code Playgroud)
我试着深入研究源代码,在ActiveRecord,ActiveAdmin和Ransack lib中的代码段上移动断点,并发现该关系是在Arel :: Nodes :: Equality的帮助下构建的.我不确定这是原因,但我可以肯定地说:
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`
Run Code Online (Sandbox Code Playgroud)
self 这是Account的Active Record关系;
在呼叫build_where#565行之前,self.to_sql等于
SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL ORDER BY "accounts"."created_at" desc
Run Code Online (Sandbox Code Playgroud)
在调用它并将结果分配给之后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
Run Code Online (Sandbox Code Playgroud)
任何关于此事的帮助或信息表示赞赏!谢谢!
所以我找到了解决方案:
首先,我已经改变了我的过滤器,admin/account.rb从
filter :by_active_projects_eq
Run Code Online (Sandbox Code Playgroud)
到
filter :by_active_projects_in,
:as => :string
Run Code Online (Sandbox Code Playgroud)
这种方法导致正确的 SQL 生成,
"accounts"."id" IN ('e4d247ec-e64d-4e8a-996a-4d73ccb11257', 'bcb8fa61-4a53-4b45-8954-8fb6ae328365', '93d670b6-7b8f-4c27-91cc-e0f44c137114')
Run Code Online (Sandbox Code Playgroud)
在那之后,我也不得不改变我的掠夺者
ransacker :by_active_projects, formatter: proc{ |v|
data = Account.having_active_projects(v).map(&:id)
data ||= nil
} do |parent|
parent.table[:id]
end
Run Code Online (Sandbox Code Playgroud)
到
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
Run Code Online (Sandbox Code Playgroud)
因为它的实现方式也导致了错误的查询:例如,没有这样的帐户正好有 5 个活动项目。在这种情况下
data = Account.having_active_projects(v).pluck(:id)
Run Code Online (Sandbox Code Playgroud)
返回“空数组”,并处理这个数组而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
Run Code Online (Sandbox Code Playgroud)
请注意"accounts"."id" IN ()造成麻烦的部分。
替换data ||= nil为 后data.present? ? data : nil,如果data不存在,则为其分配一个nil,并正确生成 SQL 中的该部分:"accounts"."id" IN (NULL)
| 归档时间: |
|
| 查看次数: |
1350 次 |
| 最近记录: |