ActiveAdmin custom filter from model attributes on the fly

时间:2015-10-06 09:05:34

标签: ruby-on-rails activeadmin

I am updating an old ruby\rails application that has an ActiveAdmin component (ActiveAdmin 0.6, Ruby 1.9.3 and Rails 3.2). The user has requested a filter that searches all fields in a given model. I don't think this is practical because you can't search a date or numeric value for "a" so I have compromised on just searching text with the filter.

Having looked at the ActiveAdmin documentation this states that you can create a filter for several attributes using "or" between the attributes. So if I wanted to search the "circumstances" or "accident_type" attributes I would use the filter below:

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

If I use this syntax the filter works as expected.

I now want to find all the string\text attributes to create by filter attributes which I did using this code (there are probably neater ways of doing this but it works):

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

I used the result to hard-code the values into the filter which gave me the following (yes there are a few attributes in the model):

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"

I tested this and it worked. All good so far. I could just leave it here but I wanted to ensure the code is self maintaining so any changes in the model would not require changes to the custom filter. This is the part I am having trouble with. I would like to change the hardcoded attributes to use the results of the code that creates the filter attributes somehow. Something like this:

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

I expect that I would need something that checks that attributes are returned otherwise the filter would fail. I can add that once I get the code working.

Appreciate any help or suggestions.

1 个答案:

答案 0 :(得分:3)

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

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

应用/管理/ notifications.rb

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

应用/模型/ 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

<强>配置/ application.rb中

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

2)将包含在可搜索关注点和方法调用中的include添加到通知模型

应用/模型/ 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

那似乎有效。我可能应该将可搜索的内容重命名为更好的内容,但它有效。