Rails 3 Dynamic Escaped Query错误的绑定变量数

时间:2012-05-24 19:07:39

标签: arrays ruby-on-rails-3 dynamic

我有一个表格,用户可以使用多种不同类型的组合以各种方式进行过滤。有了我从用户那里获得的所有输入数据,我从SQL中逃脱用户数据非常重要,这导致了我现在遇到的问题。我有两个基于发送到操作的参数动态构建的数组,一个数组包含SQL子句,另一个包含要与其相应的caluse配对的值...例如......

def results

  sql_clauses = Array.new
  sql_args = Array.new

  unless params[:elapsed_time].nil?
    sql_clauses << "elapsed_time = ?"
    sql_args << params[:elaped_time] 
  end
  unless params[:age_greater_than].nil?
    sql_clauses << "age > ?"
    sql_args << params[:age_greater_than]
  end
  .....
  @results = Model.where(sql_clauses.join(" and "), sql_args.join(", "))

end

现在这将sql_clauses数组发送到where方法没问题。但它在第二个参数上爆炸,因为它返回一个字符串,并且它期望各个变量与每个“?”相对应。出现在sql_clauses数组中的字段。我已尝试过KandadaBoggu在Comine arrays of conditions in rails上提供的解决方案。这些选项都不适合我,但可能是因为我使用的是2个数组而不是1个。

有人知道我的问题的解决方案吗?

2 个答案:

答案 0 :(得分:5)

无需使用字符串在Rails 3中构建动态查询.ActiveRecord方法(如select,where,order,limit等)返回ActiveRecord :: Relation对象,这些对象可以链接而不会触发多个数据库调用:

cars = Car.where(:colour => 'black') # No database queries are generated here.
rich_ppls_cars = cars.order('cars.price DESC').limit(10) # Still no db queries.

当我们在ActiveRecord :: Relation对象上调用.all.first.last.each时,将会查询数据库。

示例代码

假设您正在使用以下列查询模型:

  • 名称
  • ELAPSED_TIME
  • 年龄

你有一个看起来像这样的params哈希:

{ :elapsed_time => 34, :age_greater_than => 14, :max_rows => 20 }

您的控制器操作可能如下所示:

def results
    query = ModelName.select([:name, :elapsed_time, :age])
    query = query.where(:elapsed_time => params[:elapsed_time])  if params[:elapsed_time].present?
    query = query.where('age > ?', params[:age_greater_than])  if params[:age_greater_then].present?
    query = query.limit(params[:max_rows])  if params[:max_rows].present?
    @dynamic_query = query
end

请注意,我正在使用.present?测试是否存在params键。这可以防止我们将params散列中的空字符串误解为有效数据。

参考资料

答案 1 :(得分:1)

尝试这样的事情:

def results
  sql_clauses = []
  sql_clauses << "elapsed_time = :elapsed_time" if params[:elapsed_time]
  sql_clauses << "age > :age_greater_than" if params[:age_greater_than]
  .....
  @results = Model.where(sql_clauses.join(" and "), params)
end

where支持各种用于构建条件的差异语法。

如果你有很多可能包含的文件,我会以某种方式制作一个映射字典,如下所示:

map = {
   :elapsed_time => "elapsed_time =",
   :age_greater_than => "age >"
}

然后循环通过params键并构建map中存在的子句。