Topic: Find :conditions must be explicit?

Creating a new instance of an ActiveRecord object (potential record) seems pretty straightforward:

    @bird = Bird.new(params[:bird])

where all of the parameters in a form are represented by the symbol :bird , which I'll assume is a sort of "representation" for a hash of those parameters. (Stop me when I start sounding like an idiot.)

Can this also be used to do a find?  To wit:

    @bird = Bird.find(params[:bird])

Intention here is to find any matching records from any number of fields (logical AND) but effectively ignoring any blank fields.

I guess I'm trying to get a handle on the generalities before I get more specific.

Many thanks,

Charlie

Re: Find :conditions must be explicit?

I've wished that could be done for a long time.

What you CAN do, though this looks uglier, is the following:
[code lang=ruby]
Bird.find(:first, :conditions => params[:bird].collect {|k,v| "#{k}=#{Bird.quote(v)}" }.join(" AND ") )
[/code]

Re: Find :conditions must be explicit?

Something like this will be added (currently in edge rails). But really, there are so many variables in how you want the search to perform, the best option is to roll your own for the most control. For example, do you want to search only part of the column? Do you want to split the words up into keywords so the words don't have to be next to each other? etc.

Here's something to get you started. You can put this in your model.

def self.search(fields = {})
  conditions = []
  column_names.each do |column|
    conditions << "#{column} = #{sanitize(fields[column])}" unless fields[column].blank?
  end
  find(:all, :conditions => conditions.join(' and '))
end

And call it like this in the controller.

@birds = Bird.search(params[:bird])

Last edited by ryanb (2006-08-09 16:08:03)

Railscasts - Free Ruby on Rails Screencasts

Re: Find :conditions must be explicit?

Wow.  That's quite helpful.  (Thanks for holding my hand while I get my head around this stuff. I'm not even sure if I'm asking the right questions yet.)

It took me quite some time to figure out that the search method was supposed to be in the model and not the controller, despite the fact that you said "model" and it repeatedly told me the method was undefined for the class.  Seems obvious now, of course.  Is this why the definition needed to be "self.search" instead of "search" ?

I had to put single quotes around the "column = content" part of the method; I hope that's right.  (It doesn't throw an error as before, but I haven't rendered anything yet, so....) I'll also try making minor modifications to this for a similar search LIKE method.

As a follow-up, is there a reasonable way of passing the resulting instance (@bird) to the paginate method back in the controller?  In theory it would be good to use the same "list" rhtml file that already exists when all records are returned.  I see that it's possible to put conditions on the "paginate", but we just set those conditions in the model... so I'm not seeing the obvious answer anywhere.

Thanks again,

Charlie

Re: Find :conditions must be explicit?

I think you may be looking for this Paginating Collections.

Woohoo, my tut is useful!

Re: Find :conditions must be explicit?

cellmaker wrote:

Is this why the definition needed to be "self.search" instead of "search" ?

Specifying "self." at the beginning of a method definition makes it a class method. This means the method can be called from the class as in "Bird.search". If you didn't specify "self." you would create an instance method. This means you would need to create a Bird instance and call search on that, like "Bird.new.search" but that doesn't really make sense - it should be a class method because it doesn't have anything to do with one single bird object.

cellmaker wrote:

I had to put single quotes around the "column = content" part of the method; I hope that's right.  (It doesn't throw an error as before, but I haven't rendered anything yet, so....)

Sorry, the sanitize_sql method should be just "sanitize" and you shouldn't need any single quotes as it already adds them:

conditions << "#{column} = #{sanitize(fields[column])}" unless fields[column].blank?

Last edited by ryanb (2006-08-09 16:05:08)

Railscasts - Free Ruby on Rails Screencasts

Re: Find :conditions must be explicit?

Thabenksta -

That's exactly what I was looking for.  Now I just have to understand it, hehe.  (I remember writing a ridiculously complicated function in php to do the whole paginate thing in the past; as with all of my functions, I'm sure there was a much more efficient algorithm that somebody else had already done but which I didn't bother to/couldn't find).  Many thanks.

Ryanb -

Thanks for the heads-up on sanitize.  I'll make that change and test it.

On the instance/class front, I'm wading through this.  I'm sure it's obvious to most people, but I guess I'm not most people.  I was wandering through "Why's Poignant...", which you probably know, and the scope/accessor issues around instance variables and local variables seemed to be at least as important as the concept of "all objects of this type" vs. "this instance."  So there was a lot of "@thing = thing" so that you could "get at" thing outside of the class definition.  I suppose this makes sense, in that you want to be messing with the offspring, not the mother ship.  (Something like that.) 

I'm still more-or-less mystified as to when you can and can't use symbols, or even if they refer to a class variable or an instance variable.  The concept "the thing so named" isn't particularly helpful when trying to figure out if you can say :bird vs. @bird or bird.  Perhaps it's all in the use.

Thx +