#37
May 28

Simple Search Form

A search form is quite different than other forms, this is because it does not deal with model's attributes. See a good way to add a simple search form in this episode.
Tags: views forms
Download (18.4 MB, 6:28)
alternative download for iPod & Apple TV (9.6 MB, 6:28)
<!-- projects/index.rhtml -->
<% form_tag projects_path, :method => 'get' do %>
  <p>
    <%= text_field_tag :search, params[:search] %>
    <%= submit_tag "Search", :name => nil %>
  </p>
<% end %>
# projects_controller.rb
def index
  @projects = Project.search(params[:search])
end

# models/project.rb
def self.search(search)
  if search
    find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
  else
    find(:all)
  end
end

41 comments:

Zubin May 28, 2007 at 01:02

Thanks Ryan, another great episode, much appreciated!


weskycn May 28, 2007 at 01:12

good


Trueke May 28, 2007 at 01:37

Simple and powerfull. Nice tip. :)


creatop May 28, 2007 at 04:30

Nice, but what about more complicated non-model forms with many fields with validation required?


rachid May 28, 2007 at 05:14

thank u very much for your tips


Michael J. May 28, 2007 at 08:04

How about adding some ajax stuff such as a dynamicly generated drop-down box for the search field showing contents of a dictionary or project names based on the entered substring?

And PLEASE tell us something about deployment. A sample lighty or mongrel based setup would be nice. Thanks a lot for the excellent screencasts!


Rob May 28, 2007 at 09:22

What about showing how to search by booleans like 'AND', 'OR', etc.. or is that too complex to show in these casts?

Or what about doing an example that shows how exactly to get timezones setup and show some examples of using them? I found some good examples online but a screencast might be better.


Lisa May 28, 2007 at 10:21

You made a grave faux pas by not sanitising (html_escape, h) the search parameter when displaying it into the textbox after the seach; this is an avenue for XSS attacks.


Justin Ko May 28, 2007 at 10:41

I appreciate these episodes, very giving of you!


Emil May 28, 2007 at 11:26

@Lisa: Rails is doing that automatically.


Josh Peek May 28, 2007 at 16:31

Check out my simple search plugin that adds similar functionality to a model.

http://svn.joshpeek.com/projects/plugins/searchable/


Manuel May 29, 2007 at 07:01

Nice one! One idea: While coding I often think: Haven't you seen a clever hack for this in some recent railscast episode? So I find myself loading the movie again just to lookup some code snippet. What about adding the source snippets to the episode page, maybe folded to avoid clutter...?


pimpmaster May 29, 2007 at 07:53

Loving the simplicity of this screencast. I hope you do a part 2 where the user can select from a dropdown menu to filter results by different fields.

PS - I have to second Manuel's idea. Snippets would totally rock.


Ryan Bates May 29, 2007 at 08:20

I'll try to work on adding code snippets, notes, and links/resources to the episodes. Also thanks for the suggestions for future episodes everyone. :)


Michael D. Ivey May 29, 2007 at 12:05

I've been doing index() searches like this for a while now, and it never occurred to me to set the value of the text_field to the params, to make repeated searches nicer. Great idea, and it just went into my app. Thanks!


David Parker May 29, 2007 at 16:17

Yet another great screencast Ryan!

I just finished Peepcode's TDD screencast and I have to say, wow. Perhaps you can do some heavy duty TDD episodes in the future?


David Parker May 29, 2007 at 20:09

Okay, so I'm working through this screencast and I'm getting some errors... basically, everything looks okay and I can do a 'blank' search, but once I put something in it, I get the following:

undefined local variable or method `params' for Company:Class

#{RAILS_ROOT}/vendor/rails/activerecord/lib/active_record/base.rb:1233:in `method_missing'
#{RAILS_ROOT}/app/models/company.rb:13:in `search'
#{RAILS_ROOT}/app/controllers/companies_controller.rb:5:in `index'

All my code is in the same places (and looks the same) as yours does in the tutorial. Help?

Also, how would I go about writing tests for this if I was to do this (add this functionality) in a TDD way?


Ryan Bates May 29, 2007 at 21:18

@David, check your model. It looks like you are still referencing "params" in there. Notice I removed the "params" reference when moving it to the model.


David Parker May 30, 2007 at 04:38

Oh thanks! It was the last thing you deleted on the model... I must have blinked!


Matt P May 30, 2007 at 10:06

Thanks Ryan -- these screencasts are teaching me how to think in a rails way. Embarrassed to say that I have heard your voice in my mind when I'm coding my own project.


Oliver May 31, 2007 at 11:34

Thank you very much for this great podcast. And it made some things a lot easier, I already started to something like this /projects;search thingy and so on. But this one is much easier...


sthapit Jun 13, 2007 at 12:54

Awesome screencast, thanks again Ryan! 2 quick questions:

1. I like the idea of moving the search into the model but I'm using paginate like this:

@courses = Course.paginate(:all, :conditions => [...],
:page => params[:page], :per_page => 10)

I can't figure out how to use paginate and also move the search into the model.

2. I want to be able to search by dates, but I couldn't find a date_select_tag so I ended up using form_for and form.date_select which makes the URL very messy in addition to messing up paginate. I was wondering how you would implement a search like the one you showed based on dates (maybe to show when your projects are due instead of just a keyword?).

Thanks!!


Ryan Bates Jun 13, 2007 at 13:50

@sthapit, good questions.

1. This is a problem I have with many pagination solutions out there. There's a few ways to solve it.

One way is to call the class method "search_conditions" and instead of having it do the find it will just return a conditions array so you could use it in the paginate method.

Course.paginate(:all, :conditions => Course.search_conditions(..))

Another is to call the method "paginated_search" and have it call "paginate" instead of "find".

Lastly you could have the search method accept a block which uses with_scope to set the find conditions. This way you could call "paginate" in that block and the conditions will automatically be applied.

If you need a more detailed explanation, I encourage you to make a post on railsforum.com and I'll try to reply there.

2. There is the select_date method which kind of works. However, IIRC, it still puts the date in a second hash so it won't be passed well in a GET request. You may need to make your own date selectors or just use a text field. Again, if you ask on the railsforum.com someone may have a better solution.


kjdash Aug 13, 2007 at 13:41

Ryan, great screencasts!

There is a typo in the code listing for this page. You are missing a single quote in

:conditions => [name LIKE ?'


Ryan Bates Aug 13, 2007 at 20:48

Thanks kjdash, it's fixed now.


Andres Nov 02, 2007 at 18:29

I dont know where do you place the projects_path, or what it is.

Great tutorial!


unrestfulinjapan Nov 16, 2007 at 10:20

This is awesome.... but it's not working for me. When you said use "restful routes" discussed in earlier railscasts, you meant map.resources, right? I added that, but now I get an error. I can show more if this isn't enough. And yes, "aoifna" is always what I use to test form inputs. Thanks for the railscasts. I use them often

Here's the error:

NameError in MoviesController#index

uninitialized constant MoviesController

RAILS_ROOT: script/../config/..

Request

Parameters: {"search"=>"aofina"}

Show session dump

---
flash: !map:ActionController::Flash::FlashHash {}

Response
Headers: {"cookie"=>[], "Cache-Control"=>"no-cache"}


unrestfulinjapan Nov 16, 2007 at 10:23

@Andres: I think that you have to edit the routes.rb file in the config folder so that it has:

map.resources :project

in it... but I'm not 100% on that, because I'm still getting an error.


Waild Dec 15, 2007 at 12:24

Hi Ryan,

Could you please explain what is projects_path?

It is not very clear for me.
Yours,


Some1 Dec 23, 2007 at 03:27

Ryan,

When you search about other word not in the list, it will show you blank page.

How I can avoid blank results?

Your advice please.


cover Dec 25, 2007 at 13:38

@Some1

You can do something like:

def index
if params[:search]
@projects = Project.find(:all, :conditions => ["name LIKE ?", "%#{params[:search]}%"])
if @projects.size.zero?
@projects = Project.find(:all)
end
else
@projects = Project.find(:all)
end
end


cover Dec 25, 2007 at 13:40

Or even better you can also show a notice, so will be:

def index
if params[:search]
@projects = Project.find(:all, :conditions => ["name LIKE ?", "%#{params[:search]}%"])
if @projects.size.zero?
flash[:notice] = "No result found"
@projects = Project.find(:all)
end
else
@projects = Project.find(:all)
end
end


David Poynter Jan 23, 2008 at 22:01

Hey Ryan, these railcasts are a great help to me. Is there any chance that you could do one with a search entry form that is not part of the index?


Ryan Bates Feb 29, 2008 at 12:07

@David, are you referring to some kind of separate "advanced search" page? With this you can create a custom REST action as shown here:
http://railscasts.com/episodes/35

I think it's acceptable to create a "search" action which behaves similar to index except it is dedicated to searching.


eddy Mar 07, 2008 at 10:50

hey buddy ... look am just trying to build a search form for the company i work for... now this is just the simplest of search forms i require.... just so that we can locate the site map and areas of this site... by typing the area code or name... i dont have any experience in this... do u know how i can download something like that or create it in a very basic format yet effictive... please get back to me as soon as possible.... i would appreciate ur help


Nate Mar 28, 2008 at 12:29

Thank you! I have been looking for this type of tutorial for, like, two weeks now! Bless you my son!
one quick question, though. Is there a way to combine this with pagination? I think I kind of know how that would work with scoping and what not, but I'm not sure.


Ernie Apr 08, 2008 at 10:24

Ryan,


Ernie Apr 08, 2008 at 10:29

Ryan,

First, please delete the previous comment -- not sure what happened!

Second, I've been enjoying Railscasts for a while. It's a great service you do here, and the site was instrumental in picking up the basics of Rails.

You might be interested in a post I did that does a slightly more complex version of a model search, which I still call "simple model search." It's not as simple as the one you provided here, but it does actually let you largely duplicate your data entry forms, access a "Search" model instead of the normal model you'd call with form_for.

Anyway, it's at http://thebalance.metautonomo.us/2008/02/07/simple-model-search-with-rails/ if you're interested.


Jesse Apr 17, 2008 at 17:04

It took me awhile to figure out what projects_path is referring to. It's a variable that is created by adding the line "map.resources :projects" to the routes.rb file. Note that :projects refers to the model name, and that it flows through to the variable. So, if the model is called "visitors", the routes.rb file would you the symbol :visitors and the variable name would be visitors_path.

I hope this helps.

Thanks for the great screen cast. I found it really helpful.


Shikha May 01, 2008 at 21:08

How can we pass get parameters when user navigates to another pages using will_paginate?


Ernie May 02, 2008 at 09:41

Shikha,

The example I provided in my comment above, http://thebalance.metautonomo.us/2008/02/07/simple-model-search-with-rails/ , shows the use of will_paginate. If you use get instead of post (as you should anyway, on an index action) you will end up with those search parameters as part of your URL.

Add your comment:

(required)

(not displayed)

(SKIP THIS ONE)


(required)

subscribe:
sponsored by:
if you want to help:
required:
Get Quicktime Player