Building Active Record Conditions in an array - private method 'scan' called error
I'm attempting to build a set of conditions dynamically using an array as suggested in the first answer here: One or more params in model find conditions with Ruby on Rails. However I seem to be doing something incorrectly and I'm not sure if what I'm trying is fundamentally unsound or if I'm simply botching my syntax.
I'm simplifying down to a single condition here to try to illustrate the issue as I've tried to built a simple Proof of concept along these lines before layering on the 5 different condition styles I'm contending with.
This works:
excluded.push 12
excluded.push 30
@allsites = Site.all(:conditions => ["id not in (?)", excluded])
This results in a private method 'scan' called error:
conditionsSet = []
excluded.push 12
excluded.push 30
conditionsSet << ["id not in (?)", excluded]
@allsites = Site.all(:conditions => conditionsSet)
Thanks for any advice. I wasn't sure if the proper thing was to put this as a followup item to the related question/answers I noted at the top. Since I've got a problem not an answer. If there is a better way to post 开发者_开发百科this related to the existing post please let me know.
Try this:
Rails 2.3
class Site < ActiveRecord::Base
def self.build_conditions(ids, name=nil, state=nil)
cond = []
cond << send(:sanitize_sql_array, ["id NOT IN (?)", ids]) unless ids.empty?
cond << send(:sanitize_sql_array, ["name = ? ", name]) unless name
cond << send(:sanitize_sql_array, ["state = ? ", state]) unless state
cond.join(" and ")
end
end
Now somewhere in your controller:
Site.all(:conditions => Site.build_conditions([1,2]))
Site.all(:conditions => Site.build_conditions(nil, "ABC"))
Rails 3
class Site < ActiveRecord::Base
def self.exclude_ids_by_name_and_state(ids, name=nil, state=nil)
result = scoped
result = result.where("id NOT IN (?)", ids) if ids.present?
result = result.where(:name => name) if name.present?
result = result.where(:state => state) if state.present?
result
end
end
Now somewhere in your controller:
Site.exclude_ids_by_name_and_state([1,2])).all
Site.exclude_ids_by_name_and_state(nil, "ABC").all
You want:
conditionsSet += ["id not in (?)", excluded]
instead of:
conditionsSet << ["id not in (?)", excluded]
+=
adds the two arrays together (think of it as merging the two into one array) while <<
pushes a new element onto the array. So you are getting: [["id not in (?)", excluded]]
when using <<
, and :conditions
wants an array where this first element is a string (not an array).
Try SmartTuple, it's designed specifically for cases like this.
def self.build_conditions(options = {})
[
SmartTuple.new(" AND "),
(["id NOT IN (?)", ids] if options[:ids].present?),
({:name => options[:name]} if options[:name].present?),
({:state => options[:state]} if options[:state].present?),
].sum.compile
end
...
Site.all(:conditions => Site.build_conditions(:ids => {1,2]))
Site.all(:conditions => Site.build_conditions(:name => "abc", :state => "disabled")
To me it's also preferrable to use options
hash instead of ordered arguments. As your project grows, more conditions may appear and you'll lose track of which comes which. Hash looks and behaves clearer plus you can easily validate it to avoid software errors.
精彩评论