【问题标题】:Activeadmin custom filter failsActiveadmin 自定义过滤器失败
【发布时间】:2019-04-16 16:26:54
【问题描述】:

在activeadmin中,我们需要在我们的Score模型的索引页面上应用一种复杂的过滤器。也就是说,我们希望在Score 表中获得结果,使得他们的ExportOrders 具有选定的DistributionChain。我们从以下模型开始:

class Score < ApplicationRecord
  has_and_belongs_to_many :export_orders, join_table: :scores_export_orders
end

class ExportOrder < ApplicationRecord
  belongs_to :distribution_chain
  has_and_belongs_to_many :scores, join_table: :scores_export_orders
end

class DistributionChain < ApplicationRecord
  has_many :export_orders
end

在 schema.rb(摘录)中:

  create_table "scores_export_orders", id: false, force: :cascade do |t|
    t.integer "score_id"
    t.integer "export_order_id"
    t.index ["export_order_id"], name: "index_scores_export_orders_on_export_order_id", using: :btree
    t.index ["score_id"], name: "index_scores_export_orders_on_score_id", using: :btree
  end

  create_table "export_orders", force: :cascade do |t|
    t.integer  "distribution_chain_id"
  end

在分数 Activeadmin 中:

ActiveAdmin.register Score
  filter :delivery_distr_chain, as: :select, collection: DistributionChain.all
end

过滤器的范围在分数模型中定义:

class Score < ApplicationRecord
  ...
  scope :delivery_distr_chain, -> (input) {
    self.joins(:export_orders).where('export_orders.distribution_chain_id = ?', input)
  }

  def self.ransackable_scopes(auth_object = nil)
    [:delivery_distr_chain]
  end
end

以下是错误:

ActionView::Template::Error (PG::DuplicateAlias: ERROR:  table name "scores_export_orders" specified more than once
: SELECT COUNT(DISTINCT count_column) FROM (SELECT  DISTINCT "scores"."id" AS count_column FROM "scores" INNER JOIN "scores_export_orders" ON "scores_export_orders"."score_id" = "scores"."id" INNER JOIN "export_orders" ON "export_orders"."id" = "scores_export_orders"."export_order_id" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') LIMIT $1 OFFSET $2) subquery_for_count):
    1: insert_tag renderer_for(:index)

activerecord (5.0.7) lib/active_record/connection_adapters/postgresql_adapter.rb:600:in `async_exec'

我感觉查询没有正确编写。你能告诉我们我们哪里失败了吗?


编辑:值得注意的是在 rails 控制台中执行查询和在Score 模型中执行查询之间的区别。

如果我在 Rails 控制台中执行相同的查询,它可以正常工作。 Score.joins(:export_orders).where('export_orders.distribution_chain_id = ?', '2').to_sql 的输出为:

"SELECT \"scores\".* FROM \"scores\" INNER JOIN \"scores_export_orders\" ON \"scores_export_orders\".\"score_id\" = \"scores\".\"id\" INNER JOIN \"export_orders\" ON \"export_orders\".\"id\" = \"scores_export_orders\".\"export_order_id\" WHERE (export_orders.distribution_chain_id = '2') ORDER BY \"scores\".\"created_at\" DESC"

而完全相同的查询在 Score 模型的范围内失败,.to_sql 输出为:

"SELECT DISTINCT \"scores\".* FROM \"scores\" INNER JOIN \"scores_export_orders\" ON \"scores_export_orders\".\"score_id\" = \"scores\".\"id\" INNER JOIN \"export_orders\" ON \"export_orders\".\"id\" = \"scores_export_orders\".\"export_order_id\" LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id WHERE (NOT (scores_export_orders.score_id IS NULL)) AND (export_orders.distribution_chain_id = '2') ORDER BY \"scores\".\"id\" desc"

主要区别在于作用域引入的左连接:LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id,它可以再次指定scores_export_orders 表。我不知道为什么它是在里面而不是在控制台上引入的......

【问题讨论】:

    标签: sql ruby-on-rails postgresql filter activeadmin


    【解决方案1】:

    解决了。我不知道我已经在我的 ActiveAdmin 中为分数声明了一个范围,如下所示:

    scope :exported, -&gt; { joins("LEFT JOIN scores_export_orders ON scores.id = scores_export_orders.score_id").where.not("scores_export_orders.score_id IS NULL").distinct

    在 ActiveAdmin 中调用为

      controller do
        def scoped_collection
          end_of_association_chain.exported
        end
        ...
      end
    

    现在我已经重写了我的分数模型中的代码:

    class Score < ApplicationRecord
      ...
      scope :exported, -> { joins("INNER JOIN scores_export_orders ON scores.id = scores_export_orders.score_id INNER JOIN export_orders ON export_orders.id = scores_export_orders.export_order_id").where.not("scores_export_orders.score_id IS NULL").distinct }
      ...
      scope :delivery_distr_chain, -> (input) {
        self.where('export_orders.distribution_chain_id = ?', input)
      }
    
      def self.ransackable_scopes(auth_object = nil)
        [:delivery_distr_chain]
      end
    end
    

    这样我就可以在 exported 范围内进行双重连接,而无需在过滤器范围内再次加入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      相关资源
      最近更新 更多