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'
Related
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
I'm trying to build a simple inventory app, and I was wanting to have a page with a list of items, with only a name label and an editable box to update the instock amount and a single update button to update them all.
My model is named item and with two fields, name and instock
I'm not sure if you need more info, thanks.
* After playing around with it, I'm able to build the form, but when I click on update, it gives the error:
Couldn't find Item with ID=edit_multiple
Here is part of my controller:
def edit_multiple
#items = Item.find(params[:id])
And here is my routes.rb
resources :items do
collection do
get :search
post :edit_multiple
put :update_multiple
end
if anyone has any pointers or help, I'd appreciate it.
thanks,
Check out these screencasts:
http://railscasts.com/episodes/165-edit-multiple
http://railscasts.com/episodes/198-edit-multiple-individually
I had the same problem, and solved it by manually describing the path to the controller action in the edit_multiple form, like this:
= semantic_form_for :isbn, :url => {:controller => 'isbns', :action => 'update_multiple'}, :html=>{:method=>:put} do |f|
Hey. I think I am in a mind trap here. I am using Rails 2. In the index view of my controller I set up something like
def index
#posts = Post.all
end
so that I can use #posts in my index, e.g. each-do. Id like to pass #posts to a custom made view, in where I can use the same variable again. This I want to do over a link from the index view. Something like that:
link_to "newpage", {:controller => 'posts', :action => 'newmethod', :param => #posts}
What I have created so far is a new method in my Post controller. A new view. And and a new route to that site. Any suggestions? thx for your time
You're going to have to collapse those values into something that will fit in a URL, then decode them later. For instance:
# Put this in your helper method module PostsHelper
def post_ids
#posts.collect(&:id).join(',')
end
Your adjusted link would be:
link_to "newpage", {:controller => 'posts', :action => 'newmethod', :param => post_ids }
When you fetch the next page you'll need to decode these by retrieving them again:
#posts = Posts.find(params[:param].split(/,/))
There's no way to pass an instance variable between requests because they are explicitly cleared out.
As a note, try and use the generated route methods instead of the hash-style declaration. You would probably have a route already listed in rake routes:
# Instead of { :controller => 'posts', :action => 'new', :param => post_ids }
new_post_path(:param => post_ids)
These generated methods are much more readable in practice and have the advantage of being configurable later if you want to re-interpret what they mean by adjusting your routing table.
Another note is that if the list of IDs gets very large, you may not be able to encode them into a URL as the limit is about 1500 bytes. You may instead have to serialize the conditions used to generate the list in the first place and then re-run those again later. So long as you're dealing with tens of items and not hundreds you should be okay, though.
In your controller
def newmethod
#posts = Post.all
end
You can't pass all your models in a link ! The #posts var in the index action disappears after the request
I know that store arbitrary data in session is not a best practice, but in some cases this approach is simple and easy.
In your controller:
def balabala
#...
session[:your_var] = "this is the var used in another view&action!"
# ...
end
In any other pages:
<%= session[:your_var] %>
That's it. ugly, not MVC at all. :) Only recommended for very rare cases. :)
I created the following route:
map.todo "todo/today",
:controller => "todo",
:action => "show_date"
Originally, the 'show_date' action and associated view would display all the activities associated for that day for all the Campaigns.
This ended up being very slow on the database...it would generate roughly 30 records but was still slow.
So, I'm thinking of creating a partial that would first list the campaigns separately.
If someone clicked on a link associated with campaign_id = 1, I want it to go to the following route:
todo/today/campaign/1
Then I would like to know how to know that the '1' is the campaign_id in the controller and then just do its thing.
The reason I want a distinct URL is so that I can cache this list. I have to keep going back to this and it's slow.
NOTE: It's possibly the problem actually is that I've written the queries in a slow way and sqlite isn't representative of how it will be in production, in which case this work-around is unnecessary, but right now, I need a way to get back to the whole list quickly.
The code above by #Damien is correct but incomplete. It should be:
map.todo "todo/today/campaign/:id", :controller => "todo", :action => "show_date"
in your views all you have to do is:
<%= link_to "Campaign 1", todo_path(:id => 1) %>
or simply
<%= link_to "Campaign 1", todo_path(1) %>
and the particular campaign id can be fetched using params[:id] in your action.
And yeah, sqlite is not production ready.
EDIT: The latter part is quite easy to implement:
However, you have to change the route slightly,
The route will now become,
map.todo "todo/today/:campaign/:id", :controller => "todo", :action => "show_date"
in your views:
<%= link_to "Campaign 1", todo_path(:campaign => "campaign", :id => 1) %>
In your todo controller, show_date action:
def show_date
#IF YOU ARE USING THIS REPEATEDLY IN LOTS OF DIFFERENT ACTIONS THEN A BETTER PLACE FOR THIS WOULD BE AS A HELPER IN application_controller.rb
if params[:id].nil? && params[:campaign].nil?
#DO SOMETHING WHEN BOTH ARE NIL,
elsif params[:campaign]!="campaign"
#DO SOMETHING WITH CAMPAIGN BEING SOMETHING OTHER THAN "CAMPAIGN"
elsif params[:campain]=="campaign" && params[:id].nil?
#DO SOMETHING WITH ID BEING NIL.
else
#FIND YOUR CAMPAIGN HERE.
end
end
Hope this helps. :)
Just with the following :
map.todo "todo/today/:id",
:controller => "todo",
:action => "show_date"
This will create the /todo/today/:id url where id is whatever you set in the url.
You can then access it in your controller with params[:id].
You might be interested in reading Rails Routing from the Outside In, particularly the section about resources.
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