I'm a bit of a newbie with Rails and I have what I believe is a pretty simple question. I'm designing a voting application. Upon pressing a button, the number of votes increases by one. The controller method code looks like:
def vote
#item = Item.find(params[:id])
#item.increment(:votes)
#item.save
redirect_to :show
end
The button has the code of:
%= button_to "Vote", :method => "Vote" %
This seems to be where my error occurs. It's trying to go to a url of /:id?method=Vote when I'm just trying to get the application to run the method then return back to the show page. Any suggestions are very appreciated! I'm convinced this is an issue of not fully understanding the concept of routing, but honestly I'm a little stumped.
:method pertains to the HTTP verb, and default is :post, but since you are updating an object, you probably want to use :put
button_to "Vote", vote_item_path(#item), :method => :put
where #item is your Item object. You also need to define your route
put 'items/:id/vote' => 'items#vote', :as => :vote_item
To clarify what is going on here, vote_item_path(#item) will generate the URL items/1/vote if #item.id = 1
Related
i'm new of rails. I use rails 3.0.5 .
I have an EMPLOYEE resource, but I would like to manage it with another extern controller (emp_profile_controller).
This extern controller (emp_profile_controller) manages some actions (index, new_employee, create_employee, edit_employee, update_employee ecc.. ) .
My routes for this controller are :
controller :emp_profile do
get 'emp_profile' => :index
get 'emp_edit_profile' => :edit_employee
put 'emp_edit_profile' => :update_employee
get 'new_employee' => :new_employee
post 'new_employee' => :create_employee
get 'emp_list' => :emp_list
end
How can i use one form to handle both Create and Update actions in this controller ?
I tried with :
form_for(#employee, :url => { :controller => "emp_profile"}) do |f|
but it doesn't work.
If i manage only one action at time (create OR update), url_for works, for example :
form_for(#employee, :url => { :controller => "emp_profile", :action => "update_employee" }
but how can i handle both actions with one form ?
Thanks for your availability and I apologize if I asked a stupid question.
EDIT
For now, i solved checking if object exist in the form file, if exist i set a variable with the UPDATE action path, else, i set a variable with the CREATE action path. So in the form_for statement i use url_for with the above variable.
<% if #employee.new_record?
action = "create_employee"
method = "post"
else
action = "update_employee"
method = "put"
end
%>
form_for(#employee, :url => { :controller => "emp_profile", :action => action }, :method => method
I don't think it is the best way but it works and i can use only one form file.
As your model name and controller name are different, you can add this line to your routes
resources :employees,:controller=>"emp_profile",:path=>"emp_profile"
Change the method names of create_employee,update_employee to create and update respectively.
And change your form_for as given below
<%= form_for #employee do |f| %>
....
<% end %>
First of all, if you want to update something, this object should exist.
How do plan to find it out, I don't know (cause there different ways, depends on background).
There are 2 ways of solving this issue.
You can just check if object exist in view file, and if exists, renfer form for update, else for create.
Other way is to do it in controller.
For example:
def create
#employee=Employee.find_by_name('Jack Black') #for example
if #employee!=nil
render :action=> 'update'
else
#employee=Employee.new(:employee)
#employee.save
end
as i understand you want to execute two different actions on the same controller using a form submitting, this is not possible, you can only execute one action using a form submitting,
because the form is reaching to an action controller that action is suppose to render some view at the end of it's execution code, if it was possible to use to actions on form submitting how rails will know which view to render??? (that's why it's not possible).
if you want to do some more code execution at the controller, the right way to it is to call a method with some code in it that you want to execute, that method should be in the model,
because it is a good practice to write all massive chunks of code in the model and leave the controller as light from code as possible :-)
hope this helps.
I need help trying to create a link that submits an edit form.
Let's say I have a list of objects
Object - Color - Own?
Ball - Red - false - [button]
Hat - Blue - true - [button]
Shoe - Green - false - [button]
When I click on the [button] I want to set "Own?" to True.
Routes
resources :toys
Controller
def edit
#toy = Toy.find(params[:id])
end
def update
#toy = Toy.find(params[:id])
if #Toy.update_attributes(params[:toy])
flash[:notice] = "Toy Updated"
redirect_to #toy
else
render 'edit'
end
end
View
<h2>Toys</h2>
<% if #toys %>
<% #toys.each do |toy| %>
<%= toy.name %> - <%= link_to 'Set Own', edit_toy_path(:id=>toy.id, :owned=>'true')%>
<br/>
<% end %>
<% else %>
None
<% end %>
This is all about how you setup your controller actions. I'm not totally sure I understand how you want to use yours, but I have a similar case that I'll show you which I think you should be able to adapt to your situation.
In my case, I have a menu button that sets a value in the session to either keep a menu panel open or closed across any views a user looks at.
First, you need a controller action that is going to do the work you're interested in. I created a "SharedController" which handles application-wide things that don't belong to any particular view or other controller.
class SharedController < ApplicationController
# Used by AJAX links to set various settings in shared views
def edit
session[:admin_menu] = params[:admin_menu].to_sym if params[:admin_menu]
session[:advanced_search] = params[:advanced_search].to_sym if params[:advanced_search]
render :nothing => true
end
end
This controller action can set one of two values in the session, either: "admin_menu" (boolean) or "advanced_search" (boolean). Then certain views ask whether the session value for admin_menu or advanced_search is true, and if so they show the view.
You could use the same logic. Something like:
def edit
object= Object.find(params[:object_id])
object.own = params[:own]
object.save
end
To trigger this controller action with a link you need to have a route that accepts GET requests. edit is a logical choice.
resource :shared, :only => [:edit], :controller => 'shared'
Note: I think SharedController makes more sense than SharedsController, and edit_shared_path makes more sense than edit_shareds_path, so I had to specify :controller => 'shared' in my routes.rb.
Then you just need a link to a url with params. To add params onto a path you just add them to the path helper, like so:
edit_shared_path(:key => 'value')
You can retrieve these params in your controller via:
params[:key]
Make this a link like so:
link_to 'Set Own to True for This Object', edit_shared_path(:object_id=>object.id, :own=>'true')
NOTE: It's best to do this via AJAX, so be sure to set :remote=>true. If you don't use AJAX then you need to specify a redirect in your controller for what page should be loaded after this link is triggered.
In the case of my admin menu preference link, I need a link with two possible states. I generate these using a helper:
# Shows Admin Menu Button
def admin_toggle_button
if session[:admin_menu] == :on
link_to( 'Admin Tools', edit_shared_path(:admin_menu => :off), :remote=>true, :class => 'selected', :id => 'admin_toggle_button', :title => 'Hide Admin Menu' )
else
link_to( 'Admin Tools', edit_shared_path(:admin_menu => :on), :remote=>true, :id => 'admin_toggle_button', :title => 'Show Admin Menu' )
end
end
In a view I just call this using admin_toggle_button. You can do something similar if you like, but it's optional.
I hope that gets you on the right track, let me know if you have any questions.
EDIT: Based on your comment:
Links issue GET requests, which mean you're going to the EDIT action. See: http://guides.rubyonrails.org/routing.html#crud-verbs-and-actions
A further issue, you have resources :toys instead of resource :shared (which I used for this purpose). This means your link helper is already expecting a specific toy to edit, rather than handling a singular resource. See: http://guides.rubyonrails.org/routing.html#singular-resources
Your link would work if you changed it to be:
link_to 'Set Own', edit_toy_path(#toy, :owned=>'true'), :remote => true
... and set your edit action in your controller to the following:
def edit
#toy = Toy.find(params[:id])
#toy.owned = params[:owned]
if #toy.save!
head :ok
else
head :internal_server_error
end
end
See: http://guides.rubyonrails.org/layouts_and_rendering.html#using-head-to-build-header-only-responses
Now, be aware, you really should only do this with AJAX links, and you should normally not do it with your "real" controller. The reason is, now this is the only action that can be processed by EDIT, so your normal toys#edit view would no longer work.
You can get around this by create a new action and a new route, for instance:
resources :toys do
member do
get 'set_ownership'
end
end
Then simply take the same method above and call it set_ownership instead of edit. IE:
class ToysController < ApplicationController
...
def set_ownership
...
end
end
Hope that all makes sense.
The edit_toy_path method that your link_to method is calling is going to the edit action inside your controller. It's not going to the update method that I'm guessing you want.
Your link_to will need to change to something like:
<%= link_to 'Set Own', toy_path(:id=>toy.id, :owned=>'true'), :method => :put %>
But I question this particular approach. I don't think the variable will update correctly in the update action because it is not namespaced to the proper params[:toy] object that update_attributes is expecting. And in my quick and dirty tests I couldn't get it to namespace properly.
When I have a situation like the one that you are describing I usually setup another action, like toggle_ownership and I call that from my link_to with a :remote => true option. Then the controller toggles the attributes as desired.
Thus, my routes looks something like:
resources :toys do
member do
put :toggle_ownership
end
end
And my view looks like
<%= link_to 'Set Own', toggle_ownership_toy_path(toy.id), :method => :put %>
The controller sets the variable and renders back a toggle_ownership.js.erb file that updates the appropriate section of the page.
Hope that helps!
So, I have something of an odd controller/view setup, editing a Product model object occurs in the listings controller/view. There is a long-winded explanation for why this is, but I digress. However, when I submit the form, I get the error Couldn't find Product without an ID . What gives? Strangely, when I look at the params sent with the request, the ID attribute is assigned to the 'format' key. ?!.
The controller code is very simple. Edit Action:
def edit
#edit = Product.find(params[:id])
end
Update Action:
def update
#edit = Product.find(params[:id])
if #edit.save
redirect_to :url => listings_display_path
end
end
Here is my form_for code:
<% form_for #edit, :url => (listings_update_path(#edit)) do |f| %>
Edit, the trace:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"st17LW0S9uENaV8UBcxKUfRH67oo+r3TuFAxiPKMCEc=",
"product"=>{"brand"=>"test_brand",
"productname"=>"test_product",
"category"=>"test_category",
"regprice"=>"4",
"saleprice"=>"2",
"description"=>"description"},
"commit"=>"Submit",
"format"=>"21"}
Edit: routes.rb
resources :product do
resources :promotions
collection do
get "replace"
end
end
#listings
match 'listings/index' => 'listings#index'
match 'listings/display' => 'listings#display'
match 'listings/edit' => 'listings#edit'
match 'listings/update' => 'listings#update'
Edit: create action
def create
#product = Product.new(params[:product])
#product.user = current_user
if #product.save
redirect_to :action => 'index'
end
end
First, for an alternate approach to editing multiple records on a single view, try this railscast: http://railscasts.com/episodes/198-edit-multiple-individually
Second, this is unconventional but your whole approach is unconventional...
You can stick a hidden field in the form with the ID in it. Something like:
<%= f.hidden_field, :product, :id %>
Then check your params hash and the id will be in there. You should be able to access it in the controller using something similar to:
# untested
#edit = Product.find(params[:product][:id])
Off the top of my head I'm not sure how it will be stored in your params hash but it will be there and you'll be able to access it like any other hash attribute.
Good luck!
--EDIT--
Also, regarding your comment about lack of flexibility in Rails -- one thing I've learned is that Rails isn't inflexible, but it is highly optimized for particular conventions. The developers refer to this as being "highly opinionated" software, which means:
It can do just about anything you care to do it any way you want to do it, but...
There's almost always a "preferred" way which is better, faster (harder stronger lol).
You would save yourself tons of time and energy -- and probably have a lot of fun -- grabbing the Beginning Rails 3 book. You could work through it in a weekend, and when you were done you would have a great primer on the "Rails Way" which would hopefully help you go from "I don't get why Rails doesn't do this or that" to "I get how Rails works and how easy it is to do what I want to do by following X pattern". That's what happened for me anyhow.
Again good luck!
Try this
<%= form_for #edit, :url => listings_path(#edit), :html => {:method => :put} do |f| %>
You need to fix the routes that are for a single product instance to have the id:
match 'listings/index' => 'listings#index'
match 'listings/:id/display' => 'listings#display'
match 'listings/:id/edit' => 'listings#edit'
match 'listings/:id/update' => 'listings#update'
I'm working in a rails app (rails 2.3.8), and I created a resource called articles. I need a form who call the create action in that controller. I used a form_remote_tag, but I can't call correctly the "create" action, the app always call de "new" action.
I used several different combinations, the last one is this
<%=form_remote_tag :html => { :action => url_for (:action => :create)} %>
How a could do this?.
I can't use the for_remote_for or the form_for because that form will feed more tan one table in my database.
Thanks in advance
You are over complicating it:
<%= form_remote_tag :action => :create %>
Also, be sure that this is what you truly need. You say you want to create multiple objects in the form, which is why I think you may be after form_remote_for coupled with fields_for rather than form_remote_tag, but from your question I don't fully understand.
I've got a really simple rails question here but I can't seem to find the answer anywhere. I guess some of the problems stem from me following a tutorial for Rails 1.2 with Rails 2.1. Anyway..
I'm writing a blog system and I'm implementing the comments bit. I have comments displaying fine once I've created them using script/console, but getting the comment form itself working is the hard bit.
In posts_controller.rb I have
def comment
Post.find(params[:id]).comments.create(params[:comment])
flash[:notice] = "Added comment"
#render :action => show
redirect_to :action => show
end
and in show.html.erb (the view) I have
<%= form_tag :action => "comment", :id => #post %>
<%= text_area "comment", "body" %><br>
<%= submit_tag "Post Comment" %>
When I submit the form it tries to go to the urb /posts/comment/1 which is obviously incorrect, and it complains that it can't find a template. Obviously I don't want a template there because I've told it to redirect to the show action because I want it to just re-display the post's show page, with the new comment there.
I've tried both the commented out line (render :action => show) and the redirect_to line, and neither seem to do anything at all.
I'm sure I'm missing something simple, but what is it?
Does redirect_to :action => 'show', :id => params[:id] with quotes around show work?
Rails 2.1 embraces "RESTful resources". show just happens to be the name of one of the predefined REST actions that all rails controllers use.
Rails does some magic behind the scenes, and :show is equivalent to "display this one specific element with a specific given ID". Sounds like it's getting mixed up with that. The ID is probably defaulting to "1". Hence the generated URL you're seeing from the render call
The Rails 2.1 way of doing it would use the following actions and templates:
index - displays the full list of comments
create - add a new comment
show - display a specific comment only (not the full list). Doesn't sound like this is what you want, but the "magic" inside rails will default to this.
There are also actions for new (show view to enter a new comment) edit (show view to do an edit of an existing comment) update (handle update submission) and destroy (duh), but it doesn't look like you'd use them in this example.
Do you have a link to the tutorial? Wouldn't be too hard to port it to Rails 2.1 style.
yes, you use old rails style.
Something new:
form_for :comment, :url => { :post_id => #post } do |f|
f.text_area :body
submit_tag "Post"
end
you can use resources for posts and comments, search google for better tutorial or install rails 1.2.6:
gem install -v 1.2.6 rails