#37 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.
- Download:
- mp4Full Size H.264 Video (10.6 MB)
- m4vSmaller H.264 Video (7.04 MB)
- webmFull Size VP8 Video (19.4 MB)
- ogvFull Size Theora Video (14 MB)
Thanks Ryan, another great episode, much appreciated!
Nice, but what about more complicated non-model forms with many fields with validation required?
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!
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.
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.
@Lisa: Rails is doing that automatically.
Check out my simple search plugin that adds similar functionality to a model.
http://svn.joshpeek.com/projects/plugins/searchable/
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...?
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.
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. :)
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!
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?
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?
@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.
Oh thanks! It was the last thing you deleted on the model... I must have blinked!
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.
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...
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!!
@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.
Ryan, great screencasts!
There is a typo in the code listing for this page. You are missing a single quote in
:conditions => [name LIKE ?'
Thanks kjdash, it's fixed now.
I dont know where do you place the projects_path, or what it is.
Great tutorial!
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"}
@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.
Hi Ryan,
Could you please explain what is projects_path?
It is not very clear for me.
Yours,
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.
@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
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
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?
@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.
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
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.
Ryan,
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.
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.
How can we pass get parameters when user navigates to another pages using will_paginate?
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.
nice & easy for newbie like me :)
thanks :D
Nice and thanks so much had one question. I changed the search field to search for a date entered in a format of 2008-06-27 and it will return my results.
Changing this input text field to a date_select I cannot make it work. I see the three params but I continue to get
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
Will this inline type search work with date_select or select_date?
Thanks
Many thanks for the casts.
i am a newbie and i have a problem with this if someone could help i'l appreciate it.
I am using this code and added a select to search for different stuff and i would like to have only 1 method to do that, but i get a mysql error. the code is:
view -->
<% form_tag ('find', :method => 'get') do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= select :user, :type, User::USER_ATTRIBUTES %>
<%= submit_tag "Buscar", :name => nil %>
</p>
controller -->
if params[:search]
@users =User.find(:all, :conditions => ["%#{params[:user][:type]}% LIKE ?", "%#{params[:search]}%"])
i get this:
Mysql::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '%login% LIKE '%ro%')' at line 1: SELECT * FROM `users` WHERE (%login% LIKE '%ro%'
and parameters
{"user"=>{"type"=>"login"},
"search"=>"ro"}
Many thanks Ryan - you are the man! Excellent stuff. I'll be donating shortly!
Awesome! I'm a little late to the ROR party, but your Railscasts help me to improve every day. Keep up the great work!
I'm with K. You save the day!
Hey, great show. I'm very very new to RoR and still got it working!
However I'd like to make it so if no results are shown it shows a message, not display all. How is this possible in rails?
Thanks :)
Nick - Just take out the else statement, then in your view check to see if @projects is holding anything, if its no then display a no results message...
Thanks!!!! Your screencast is good and easy.
Very helpful! Thank you!
In the example above, what if you wanted the list to be hidden unless the search results matched your query?
I presume it would be something like
<% if some-code-here = nil %>
<p>No results found</p>
<% else %>
<ul>
<% for project in @projects %>
etc, etc., etc.
But I'm not sure. Thoughts?
I'm new and a bit rusty, I know it shows.
thanx
I get an error
news_url failed to generate from {:controller=>"news", :method=>"get", :action=>"show"}, expected: {:controller=>"news", :action=>"show"}, diff: {:method=>"get"}
I add
map.resources :new
to routes.rb
Model name : new
Table name : news
controller name : gamescore
help me please
Everything works great when I actually include the search term in the URL (http://localhost:3000/products?search=yard) but not when using the "normal" URL (http://localhost:3000/products). That results in a "NoMethodErorr".
How can I get the default form to work?
Oops. I was using this in combination with the "group_by" railscast and I was trying to group on a field that had two entries that were nil. I changed my find(:all) to filter out nil values and everything works now.
Is anyone can provice me with some source code of Advanced Search? Thank you very much!
Cheers for this! I've been plodding through the paper pages of pragmatic programmer books til now, this is the most enlightening rails 5 minutes I've spent : )
hi, thank you for examples, my question is that if i want to show the search results how can i show that in the same view , thank u very much:)
Hi. I love the screencasts. However, I just watched the episodes on SQL injection and XSS, and it looked like your code was susceptible to those attacks. However, I could be totally wrong, because I am very new to RoR.
Anyway, I love your simple and RESTful solutions.
Useful podcast, I was just wondering how to use other form objects and treat them differently using the 'rails' way; this making the controller behave differently due to model feedback should have been obvious but now it is. Thanks, nice cast.
Ryan,
I seem to be having trouble implementing the search. I get the following error: SQLite3::SQLException: no such column: name: SELECT * FROM "posts" WHERE (name LIKE '%Body%')
Help please?
Senthil,
You probably forgot to change column name from 'name' (as given in the railcast example) to the actual column name defined in your own DB.
So line from your model should look like this:
find(:all, :conditions => ['<<YOUR COLUMN NAME HERE>> LIKE ?', "%#{search}%"])
Greate stuff! Really simple to use, loved it! thanks a lot!
I think that you have to edit the routes.rb file in the config folder so that it ...
Thanks for the cast. I'm doing what you do but my form is not showing up. Also not in HTML. I don't use the projects_path but :controller => 'projects' :action => 'search' in stead. Is that the problem?
Rutger
thanks a lot!!
Great tutorial! But I'm using this to search for a virtual attribute. I get an error saying that theres no such column in my db table (cause its virtual..). Can this method not be used for searching virtual attributes?
Ryan I have followed your railscast to the T. However, whenever I try to access the page I keep getting a route error. The error I get is this
No route matches {:controller=>"equipment", :action=>"destroy"}. this makes no sense to me sense i am not even touching the destroy method. Please help, I have been wrestling with this for two days. Thanks
thank you for covering the basics, most tutorials forget about the existence of noobness.
too bad there is no asciicast for this, those of us with limited english mastery feel like they should complete phonetics tutorials first.
Ryan
For some reason I am having trouble mapping this to mongodb using mongoid. I believe this portion sis the culprit..
if search
find(:all, :conditions => ['name LIKE ?', "%#{search}%"])
else
find(:all)
end
Is the problem with the LIKE clause?
Any way to make it work better with UTF-8 characters? If I have "
For those experiencing the same issue, just use UPPER before the input and searchable item. So, in the screencast example, it would be 'UPPER(name) LIKE UPPER(?)'
I found that I needed to change this line:
<% form_tag projects_path, :method => 'get' do %>
to
<%= form_tag projects_path, :method => 'get' do %>
in order to get the form to appear.
This is due to a Rails 3 change:
See "7.4.2 Helpers with Blocks" in http://edgeguides.rubyonrails.org/3_0_release_notes.html
In case someone's still looking for an easy answer to searching in multiple attributes, you can set it up like the where params in scopes:
I don't have a ton of experience coding (although I've written working web apps w/ db backend in ASP years ago), so I'm probably missing something obvious, but I cannot for the LIFE of me figure out why I'm getting the following error:
undefined method `search' for #Class:0x4601c10
I'm not following this tutorial exactly but rather using a combination of this and https://we.riseup.net/rails/simple-search-tutorial, which is similar in a lot of respects. The main difference seems to be that the other tutorial uses this in the form declaration:
<%= form_tag :controller => 'sheets', :action => 'search', :method => 'get' do %>
This is because my app is set up a little bit differently. What am I missing here? I can see where the error IS (i.e. Project.search in the controller file) but I don't know how to fix it, and I've googled till I can't see straight.
Thanks.
Please copy/paste the controller's
index
action, and the model'ssearch
method.That probably means you're missing the search method in your model.
Wonderful cast! Thanks for the help
thanks for the information.. i just got knew little bit about rails.. thanks
For Rails 3 compatibility, the ActiveRecord queries in the search method should look like this:
See http://railscasts.com/episodes/202-active-record-queries-in-rails-3 and http://m.onkey.org/active-record-query-interface
yes, thanks, and if you want to use it with pagination, for me it works like this:
but later I refactor it to scope
Let's say you wanted to add sort order to the results in my comment above, then you could do this:
And then you could refactor like this:
And if you also wanted to search
description
you might do this:Next, change your controller to pass the whole
params
rather than justparams[:search])
:Then update your
search
method accordingly:Why do all this? Because if you had more fields in your search view, you could easily search them too! For instance, let's say you change the above view to also include min price and max price:
Then in the search method, you do:
And for style you could change your view to use
search_field_tag
,size
, andplaceholder
:I hope this helps someone. For more info, see: http://railscasts.com/episodes/111-advanced-search-form-revised
That was really helpful, thanks!
@Bardwin
Your example works very well. Thank you!
How to show a notice like "no record match" when there is no match record according to the input value?
Is this prone to SQL injection? It looks like you're directly embedding strings into your SQL queries.
Also, that last part about adding more fields to the search came in very handy, thanks! I was able to add a dropdown list to filter results in addition to the search.
it would really help me if there is a way to change the text_field to date_select or something in order to search db records by date.
i have a field named date among timestamps.
Hi,
I'm doing a project in rails & mongoDB ... how can I make this searching work with mongoDB ? I'm using mongoid as mapper.
When I'm searching it is giving me this error :
undefined method `expand_complex_criteria' for ["name LIKE ?", "%fd%"]:Array
Late response but it can be usefull for someone else.
There's no "LIKE" command for MongoDB, you can perform a search with a regex.
But as always there's already some gem that do the work for you :
For Keyword search : https://github.com/mauriciozaffari/mongoid_search
For FullText search : https://github.com/artsy/mongoid_fulltext
I'm just here to say, It's the year 2012 and i find this tutorial still useful, thanks Ryan! =D, I'm a student from Mexico and learning ruby on rails has become very easy thanks to your videos!
I hope you can keep doing videos for more years, thanks again and see ya ! =D
Ty, I'm trying to learn rails. When I put these lines of code in my index nothing show up, so I needed add '=' after <% in form_tag. After that all works great, ty.
<% form_tag projects_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<% end %>
<%= form_tag projects_path, :method => 'get' do %>
you need an equal sign for rails 3
<%=
instead of
<%
Hello, also you can use such scope:
scope :search, lambda { |search| where("label LIKE ?", "%#{search}%")}
as the result query you will have objects which can be paginated also, and it will not crash even if "search" will be nil.
Love this! Thanks Ryan!!
"<%= form_tag projects_path, :method => 'get' do %>
you need an equal sign for rails 3
<%=
instead of
<%"
maybe for situations like that you should add some changes for different rails versions in notes? to show how it was in rails 2 and how it is in rails 3 (in case if it is a small change only ?)
anyway, great tutorial, as all of them, thanks!
Simple and useful.Thanks.
Hello from 2013!
For rails 4 I am using the following code:
PostController:
def index
if params[:search]
@posts = Post.search(params[:search]).order("created_at DESC")
else
@posts = Post.all.order('created_at DESC')
end
end
Post model
def self.search(query)
# where(:title, query) -> This would return an exact match of the query
where("title like ?", "%#{query}%")
end
Index view:
<%= form_tag(posts_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Posts" %>
<%= submit_tag "Search" %>
<% end %>
Good luck!
Hello!
This works great!
How can i add to the search query a second variable from the same model?
i used
def self.search(query)
where("title like ?", "%#{query}%")
end
but i also have a little description (post.description) in the index view and i want to add it to the search query.
Ty very much
As the following:
where("firstname ilike :q or lastname ilike :q ", q: "%#{query}%")
@Denis
Thanks for the Rails 4 updated syntax!!
Really helped me out.
what about "/projects/yardwork" ?
How should I search with this pretty URL?
@grammakov: You are a bloody lifesaver! Thanks for posting rails 4 compatible code.
I am having problem with the search function.when search function is assigned the page will reload and can't find out the search element.How to solve this error?.
I new to rails. I don't know if it was possible to use form_tag in view without loud erb tags in earlier or not. But in rails 4 it is not working so code we need to write for this is :-
<%= form_tag('/products', method: :get) do %>
<%= text_field_tag :search %>
<%= submit_tag "search" %>
<% end %>
Happy learning :)
For anyone who's using Rails 4 and is wondering about
projects_path
, don't worry. I'm a Rails newbie, but in my experience, Rails adds what you need to your routes file (app/models/modelname.rb)
automatically when you create the controller.If it doesn't, I believe adding the following line does it:
Also, if you're wondering why we use
def self.search
instead ofdef search
, it's because we want a class method instead of an instance method. In other words, it allows us to doProject.search
, we're calling the method from the class, not from an instance of the class.Also, you can use
search_field_tag
instead oftext_field_tag
. That will create a text field of type "search".You could also check out my gem 'attr_searchable'
https://github.com/mrkamel/attr_searchable
which adds support for search engine like queries to ActiveRecord models and can exploit fulltext indices for mysql/postgres.
Hi i have to search the attributes which is there in some other model and i want to search multiple attributes
index view
<%= form_tag issue_slips_path, :method => 'get' do %>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
<%end%>
controller
model
def self.search(search)
if search
find(:all, :conditions => ['store_location_id LIKE :search ', {:search => "%#{search}%"}])
else
find(:all)
end
end
the store location name is what i have to search but i have only the id, how to search the name which is there in another StoreLocation model please help
You can also get this function here
http://quickwebinfo.com/blog/2015/06/create-a-search-engine-function-on-your-php-site
I hope you will like.
This has been updated to Rails 5 as a blog post. Simple Search Form in Rails 5
Hi, I believe the form tag is missing the '=', otherwise the form does not show in html
Hey I used this great video to search my posts by tags. This serch is working:
Tag.find_by_name!(name).posts
But instead this serch is not working:
Tag.find(:all, :conditions => ['name LIKE ?', "%#{name}%"]).posts
Thanks for help
Great episode Ryan.
Fantastic article.