如何获取最后的ActiveRecord查询数据?

时间:2016-07-24 12:56:00

标签: ruby-on-rails ruby activerecord

我是Ruby on Rails的新手,所以如果问题很愚蠢,你会原谅我自己......

我有一个表和两个表单,第一个表单是过滤(通过复选框),第二个表单是搜索表。

我想要的是使用已应用的过滤器搜索表格。我真的不知道如何...我如何从控制器搜索方法访问“已经过滤”的数据。

源代码:

prices_controller.rb

def filter()
 @prices = Price.where(date: Date.today).order(price: :asc)
  if params[:filter1] != nil 
    @prices = @prices.where('filter1_field IN (?)', params[:filter1])
  end
  if params[:filter2] != nil 
    @prices = @prices.where('filter2_field IN (?)', params[:filter2])
  end
  respond_to do |format|
    format.js
  end
end

def search()
  # Here I would like to use filtered prices, not all prices
  @prices = Price.where(date: Date.today).order(price: :asc)
  if params[:search] != nil
     @prices = @prices.where('name LIKE ?', "%#{params[:search]}%")
  end
end 

它有效,但我没有使用过滤值... 有没有办法保存最后一个查询或类似的东西。我尝试返回搜索方法ActiveRecord :: Relation但该方法中不存在的方法。其他替代方法是使用相同的形式进行过滤和搜索。

我接受任何建议。

非常感谢。

2 个答案:

答案 0 :(得分:1)

这里有一个非常糟糕的错误:您的IN已损坏。

ActiveRecord执行SQL清理(以防止SQL注入),因此在您编写时:

query.where("a in ?", "hey")

在SQL端获得WHERE a in "hey"。 您的查询中的内容相同:您的IN参数将被字符串化。

如果您的filter1a,b,c,则查询将如下所示:

WHERE a iN ("a,b,c")

这根本不是你想要的。

ActiveRecord能够在传递数组时生成IN。所以这个:

query.where(a: [1, 2, 3])

将生成:

WHERE q IN (1, 2, 3)

这就是你想要的!

现在,关于代码本身......

执行所需操作的最简单方法是将代码移动到另一个方法,并在两个位置重复使用它:

def filter()
 @prices = build_filter_query(Date.today, params[:filter1], params[:filter2])
  respond_to do |format|
    format.js
  end
end

def search()
  @prices = build_filter_query(Date.today, params[:filter1], params[:filter2], params[:search]) #pass in an extra param here!
end

private
  def build_filter_query(date, filter1, filter2, search = nil)
    # use a local variable here
    prices = Price.where(date: Date.today).order(price: :asc)
    if filter1
      prices = prices.where(filter_field1: filter1)
    end
    if filter2
      prices = prices.where(filter2_field: filter2)
    end
    if search
       prices = prices.where('name LIKE ?', "%#{search}%")
    end
    return prices
  end

答案 1 :(得分:1)

你不能直接这样做,因为

  1. 应用过滤器的请求和触发搜索的请求是2个不同的请求。
  2. Rails为每个请求初始化一个新的控制器实例,因此用于过滤的控制器和用于搜索的控制器是2个不同的对象。它们不共享任何实例变量。
  3. 但是,您可以将过滤阶段返回的ID存储在会话中,并在搜索阶段使用这些ID。

    def filter
      @prices = Price.where(date: Date.today).order(price: :asc)
      if params[:filter1] != nil 
        @prices = @prices.where('filter1_field IN (?)', params[:filter1])
      end
      if params[:filter2] != nil 
        @prices = @prices.where('filter2_field IN (?)', params[:filter2])
      end      
    
      session[:price_ids] = @prices.pluck(:id)
    
      respond_to do |format|
        format.js
      end
    end
    
    def search
    
      @prices = session[:price_ids].present? ? 
                Price.where(id: session[:price_ids]) :
                Price.all
    
      @prices = @price.where(date: Date.today).order(price: :asc)
      if params[:search] != nil
         @prices = @prices.where('name LIKE ?', "%#{params[:search]}%")
      end
    end
    

    这种方法的缺点是您必须在适当的时间从会话中删除价格ID,这很难定义。

    另一种方法是确保浏览器在每次请求搜索时发送过滤器(除非您的Rails应用程序是单页面应用程序的API)。每当更改过滤器时,此方法都需要重定向。

    def filter
      redirect_to search_path(params.slice(:filter1, :filter2))
    end
    
    def search
      @prices = Price.where(date: Date.today).order(price: :asc)
      if params[:filter1] != nil 
        @prices = @prices.where('filter1_field IN (?)', params[:filter1])
      end
      if params[:filter2] != nil 
        @prices = @prices.where('filter2_field IN (?)', params[:filter2])
      end
      if params[:search] != nil
         @prices = @prices.where('name LIKE ?', "%#{params[:search]}%")
      end
    end
    

    您的搜索表单应包含2个过滤器隐藏字段

    <%= form_tag '/prices/search', method: :get do %>
      <%= hidden_field_tag :filter1, params[:filter1] %>
      <%= hidden_field_tag :filter2, params[:filter2] %>
      <!-- other form stuff ... -->
    <% end %>