我正在努力寻找对动态方法进行元编程的最佳方法,我将根据条件限制结果......所以例如:
class Timeslip < ActiveRecord::Base
def self.by_car_trans(car, trans)
joins(:car)
.where("cars.trans IN (?) and cars.year IN (?) and cars.model ILIKE ?", trans, 1993..2002, car)
.order('et1320')
end
end
假设我没有传入我的参数,而是传入一系列条件,键是字段名称,值是字段值。 例如,我会做这样的事情:
我会传入 [["字段"、"值"、"运算符"],["字段"、"值"、"运算符"]]
def self.using_conditions(conditions)
joins(:car)
conditions.each do |key, value|
where("cars.#{key} #{operator} ?", value)
end
end
但是,这行不通,而且不是很灵活...我希望能够检测该值是否是一个数组,并使用 IN () 而不是 =,也许也可以将 ILIKE 用于不区分大小写的条件......
任何建议不胜感激。我在这里的主要目标是拥有一个"列表"模型,用户可以在其中动态构建他们的条件,然后保存该列表以供将来使用。此列表将根据关联的汽车表过滤时间表模型...也许有更简单的方法可以解决这个问题?
首先,您可能会对 Squeel 宝石感兴趣。
除此之外,对 IN 或 LIKE 谓词使用 arel_table
:
joins( :car ).where( Car.arel_table[key].in values )
joins( :car ).where( Car.arel_table[key].matches value )
您可以检测值的类型以选择适当的谓词(不是很好的 OO,但仍然):
column = Car.arel_table[key]
predicate = value.respond_to?( :to_str ) ? :in : :matches # or any logic you want
joins( :car ).where( column.send predicate, value )
您可以根据需要链接任意数量的链接:
conditions.each do |(key, value, predicate)|
scope = scope.where( Car.arel_table[key].send predicate, value )
end
return scope
那么,你想要最终用户可以在运行时指定的动态查询(并且可以存储和检索供以后使用)?
我认为你走在正确的轨道上。唯一的细节是如何建模和存储标准。我不明白为什么以下内容不起作用:
def self.using_conditions(conditions)
joins(:car)
crit = conditions.each_with_object({}) {|(field, op, value), m|
m["#{field} #{op} ?"] = value
}
where crit.keys.join(' AND '), *crit.values
end
警告 上面的代码是不安全的,并且容易被SQL注入。
此外,没有简单的方法来指定AND
条件与OR
条件。最后,大多数情况下的简单"#{field} #{op} ?", value
仅适用于数值字段和二进制运算符。
但这表明这种方法是可行的,只是有很大的改进空间。