Rails链接查询

时间:2015-04-15 17:19:01

标签: ruby-on-rails model

我在过滤可选查询参数时遇到困难。

def index
  @books = Book.where(nil) 
  @books = @books.status(params[:status]) if params[:status].present?
  @books = @books.location(params[:location]) if    params[:location].present?
  @books = @books.starts_with(params[:starts_with]) if params[:starts_with].present? 
end

我在网上找到了一个非常类似的例子。我的问题是链接可选参数。例如,我只想过滤以下内容:status和:location或filter for:location和:starts_with?不知道该怎么做......

2 个答案:

答案 0 :(得分:2)

此时您可能需要降低到纯AREL并将其作为参数传递给搜索方法。这给了一些不错的额外predicates并且更具可扩展性

这是一个令人失望和肮脏的例子,你可以 用AREL做这件事。它的未经测试的代码虽然......它不应SQL injection开放,但我不记得AREL是否清理了匹配查询。匹配的AREL方法也优于原始SQL,因为它应该是DB不可知的。

class Book
  ... 
  # search class method via AREL
  def self.search(params = {})
     if params.respond_to?(:has_key?)
       # setup arel object for proper table
       books = Arel::Table.new(:books)

       if params.has_key?(:location)
         location_match = books[:location].eq(params[:location]) 
       end

       if params.has_key?(:status)
         status_match = books[:status].eq(params[:status]) 
       end

       # although if all you're doing is searching for a title
       # maybe you can deprecate starts_with column and search 
       # the proper column say... title
       if params.has_key?(:starts_with)
         title_match = books[:starts_with].matches("%#{params[:starts_with}%")
       end

       # return AREL query. Note this *should* be safe from [SQL injection][4]
       # via AREL sanitization but verify then trust. 

       # Choose one of the below
       # match on Any 
       where( location_match.or(status_match).or(title_match) )

       # match on ALL 
       where( location_match.and(status_match).and(title_match) )
    end
  end
  ...
end

或者更复杂的设置,但更简单的方法是使用search kick gem并设置elasticsearch实例。

同样快速注意。使用Hash#present检查哈希的密钥?是一种代码味道。如果被检查对象没有响应索引方法会发生什么......它会爆炸

(dev)> a = nil
=> nil
(dev)> a[:dave].present?
NoMethodError: undefined method `[]' for nil:NilClass

IMO您应该更正确地检查哈希本身是否存在,然后使用ruby方法has_key?(:some_key)......

(dev)> a = nil
=> nil
(dev)> a.present? && a.has_key?(:dave)
=> false
(dev)>

但那只是我,你的里程可能会有所不同。基本上永远不要相信ruby的类型是你所期望的。

注意在这个方法中我们设置一个默认哈希,如果没有传递,那么我们可以跳转到has_key?检查,但是如果我们传递了别的东西它仍然会消失,因此respond_to?检查参数。

现在,当您在控制器(或模型或任何地方)中调用它时,您可以这样调用

@books = Book.search(params)

答案 1 :(得分:0)

您可以执行以下操作:

def index
  @books = Book.scoped
  @books = @books.where(status: params[:status]) if params[:status].present?
  @books = @books.where(location: params[:location]) if params[:location].present?
  @books = @books.where(starts_with: params[:starts_with]) if params[:starts_with].present? 
end

但我不确定starts_with属性......