来自模型属性的 ActiveAdmin 自定义过滤器

ActiveAdmin custom filter from model attributes on the fly

我正在更新具有 ActiveAdmin 组件(ActiveAdmin 0.6、Ruby 1.9.3 和 Rails 3.2)的旧 ruby\rails 应用程序。用户请求了一个过滤器来搜索给定模型中的所有字段。我认为这不实用,因为您无法搜索 "a" 的日期或数值,所以我在仅使用过滤器搜索文本方面做出了妥协。

查看 ActiveAdmin 文档后,它指出您可以使用属性之间的“or”为多个属性创建过滤器。因此,如果我想搜索 "circumstances" 或 "accident_type" 属性,我将使用下面的过滤器:

filter :circumstances_or_accident_type, :as => :string, label: "Search All Text Fields"

如果我使用此语法,过滤器将按预期工作。

我现在想找到所有 string\text 属性,以通过我使用此代码所做的过滤器属性创建(可能有更简洁的方法,但它有效):

xfilter_text = ""
Notification.columns.each do |xfield|
  if xfield.type == :string or xfield.type == :text
    if xfilter_text.length == 0
      xfilter_text = xfield.name
    else
      xfilter_text << "_or_"
      xfilter_text << xfield.name
    end
  end
end

我使用结果将值硬编码到过滤器中,得到以下结果(是的,模型中有一些属性):

filter :circumstances_or_accident_type_or_author_type_or_location_or_immediate_action_or_injury_details_or_outcome_type_or_investigation_findings_or_action_to_prevent_recurrence_or_building_or_classification_or_manager_email_or_manager_name_or_current_stage_or_injured_last_name_or_injured_first_name_or_injured_gender_or_injured_address_or_injured_home_telephone_or_injured_work_status_or_injured_job_title_or_injured_working_pattern_or_injured_email_or_riddor_document_or_body_part_or_kind_of_accident_or_injury_type_or_service_or_team_or_defects_or_witness_details_or_location_details_or_hse_reference_number_or_riddor_category_or_address_or_details_of_treatment_or_processor_actions_or_business_unit_or_other_author_type_or_lost_time_details_or_changed_by_or_details_of_hospital_treatment, :as => :string, label: "Search All Text Fields"

我对此进行了测试并且有效。到目前为止一切都很好。我可以把它留在这里,但我想确保代码是自我维护的,这样模型中的任何更改都不需要更改自定义过滤器。这是我遇到麻烦的部分。我想更改硬编码属性以使用以某种方式创建过滤器属性的代码的结果。像这样:

filter :get_filter, :as => :string, label: "Search All Text Fields"

def get_filter
  xfilter_text = ""
  Notification.columns.each do |xfield|
    if xfield.type == :string or xfield.type == :text
      if xfilter_text.length == 0
        xfilter_text = xfield.name
      else
        xfilter_text << "_or_"
        xfilter_text << xfield.name
      end
    end
    return xfilter
  end
end

我希望我需要一些东西来检查是否返回了属性,否则过滤器将失败。一旦我让代码工作,我就可以添加它。

感谢任何帮助或建议。

我倾向于使用模型自己的 scope/class 方法生成查询并将其委托给模型。然后你只需要通知 MetaSearch/Ransack (取决于你的 ActiveAdmin 版本)它可以搜索那个范围,你可以将它添加为过滤器。

对于奖励积分,您可以将搜索方法放入可以包含在任何模型中的问题中。

app/admin/notifications.rb

filter :containing_text, as: :string, label: 'Text Search:'

app/models/notification.rb

# for MetaSearch
# search_methods :containing_text

# for Ransack
def self.ransackable_scopes(_opts)
  [:containing_text]
end

# this could be dropped into a concern as-is
def self.containing_text(query)
  # select text-type columns
  cols = columns.select { |c| [:string, :text].include?(c.type) }

  # generate query fragment
  fragment = cols.map { |c| "#{ table_name }.#{ c.name } LIKE ?" }
                 .join(' OR ')

  # execute sanitized query
  where(fragment, *Array.new(cols.size, "%#{ query }%"))
end

### OP 编辑​​###

我以前从未使用过关注点,所以最终弄清楚了如何让它工作:

1) 将关注路径添加到您的 application.rb

config/application.rb

class Application < Rails::Application
  config.autoload_paths += %W(#{config.root}/app/models/concerns)
end

2) 将 include 添加到 Searchable 关注点并将方法调用添加到通知模型中

app/models/notification.rb

include Searchable
search_methods :containing_text

3) 创建问题:

/app/models/concerns/searchable.rb

module Searchable
  extend ActiveSupport::Concern
  module ClassMethods
    def self.containing_text(query)
      # select text-type columns (string and text)
      cols = columns.select { |c| [:string, :text].include?(c.type) }
      # generate query fragment
      fragment = cols.map { |c| "#{ table_name }.#{ c.name } LIKE ?" }

                     .join(' OR ')
      # execute sanitized query
      where(fragment, *Array.new(cols.size, "%#{ query }%"))
    end
  end
end

那似乎奏效了。我可能应该将可搜索重命名为更好的名称,但它确实有效。