【问题标题】:Search for multiple possible param values搜索多个可能的参数值
【发布时间】:2020-02-16 16:55:37
【问题描述】:

目前我有一个表单,用户可以在其中输入每人价格和/或持续时间和/或团队规模。我想要完成的是从表中检索与用户输入匹配的所有记录,为此我在模型中设置范围:

scope :filter_by_team_size, -> (team_size) { where("team_size = ?", team_size) }
scope :filter_by_duration, -> (duration) { where("duration = ?", duration) }
scope :filter_by_price, -> (price) { where("price = ?", price) }

然后在控制器中再次使用它来检索记录:

@experiences = policy_scope(Experience).order(team_size: :desc).geocoded.filter_by_team_size(params[:team_size]) if params[:team_size].present?
@experiences = policy_scope(Experience).order(duration: :desc).geocoded.filter_by_duration(params[:duration]) if params[:duration].present?
@experiences = policy_scope(Experience).order(price: :desc).geocoded.filter_by_price(params[:price]) if params[:price].present?

但是,这只会给我第一个输入值匹配的记录,而忽略所有其他值。此外,当您查看搜索结果并再次使用过滤器时,它应该只对已找到的记录应用过滤器。

任何有关如何解决此问题的建议将不胜感激!

【问题讨论】:

  • 您是否尝试过在记录上使用 select 方法?所以如果你有@experiences,你可以申请@expereinces.select {|e| e.duration == params[:duration]}之类的东西吗?可能会帮助您不断过滤掉事物。 apidock.com/ruby/Array/select
  • @RockwellRice 坏主意。这会将所有记录加载到数据库中,如果您有少量记录,这将起作用,但如果您有大量数据,则会非常慢或彻底崩溃服务器。他实际上是在正确的道路上构建范围以过滤数据。

标签: sql ruby-on-rails activerecord


【解决方案1】:

处理此问题的一种方法是使用虚拟模型来处理与表单之间的绑定参数:

class SearchQuery
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :team_size, :integer
  attribute :duration
  attribute :price
end

然后您可以设置表单:

<%= form_with(model: (@search_query || SearchQuery.new), url: '/experiences', method: :get) %>
  <div>
    <%= f.label :team_size %>
    <%= f.number_field :team_size %>
  </div>
  # ..
<% end %>

然后您可以使用 ActionController::Parameters#permit 将参数绑定到模型,就像使用普通 ActiveRecord 模型一样:

class ExperiencesController
  before_action :set_search_query, only: :index, if: ->{ params[:search_query].present? }
  # ...

  def index
    @experiences = if @search_query 
      @search_query.build_scope(policy_scope(Experience))
    else
      policy_scope(Experience)
    end.geocoded
  end

  private
  def set_search_query
     @search_query = SearchQuery.new(search_query_params)
  end

  def search_query_params
     params.fetch(:search_query).permit(:team_size, :duration, :price)
  end
end

这个环回将使表单像普通的 CRUD 表单一样有状态。我们实际上还没有实现 #build_scope 是的,所以让我们这样做吧:

class SearchQuery
  include ActiveModel::Model
  include ActiveModel::Attributes
  attribute :team_size, :integer
  attribute :duration
  attribute :price

  def build_scope(base_scope)
    compacted_attributes = attributes.reject { value.nil? || value.empty? }
    compacted_attributes.each_with_object(base_scope) do |(k,v), base|
      if base.respond_to? "filter_by_#{k}"
        # lets you customize the logic with a scope
        base.send("filter_by_#{k}", v) # the scope is responsible for ordering
      else
        # convention over configuration!
        base.where(Hash[k,v]).order(Hash[k,:desc])
      end
    end
  end
end

由于这使用约定优于配置,因此您可以摆脱模型中那些无意义的作用域。

【讨论】:

    猜你喜欢
    • 2015-11-13
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 2010-09-24
    • 1970-01-01
    相关资源
    最近更新 更多