how do i pass a polymorphic object to another controller?
for example redirecting from messages/1/
to requests/new?object_type=message&object_id=1
or, second example, from files/154/
to requests/new?object_type=file&object_id=154
is
redirect_to new_request_path(:object_type => params[:controller].classify, :object_id => params[:id])
right?
Request model has
belongs_to :object , :polymorphic => true
You nest your routes, for example:
messages/1/requests/new
files/154/requests/new
redirect_to new_comments_request_path(Comment.find(1))
routes guide
Related
I have such code:
def update
#oil = Oil.find(params[:id])
#product_types = ProductType.all
if #oil.update_attributes(params[:oil])
if #oil.other_products_cross_lists.update_attributes(:cross_value => #oil.model.to_s.gsub(/\s+/, "").upcase)
redirect_to admin_oils_path
end
else
render :layout => 'admin'
end
end
but when i run it i get:
undefined method `update_attributes' for #<ActiveRecord::Relation:0x007f7fb4cdc220>
and my other_products_cross_lists isn't updated... Also i try update_attribute and get the same error.
What i do wrong?
Also when i run my destroy method
def destroy
#oil = Oil.find(params[:id])
if #oil.destroy
if #oil.other_products_cross_lists.destroy
redirect_to admin_oils_path
end
else
render :layout => 'admin'
end
end
other_products_cross_lists didn't destroy...
How can i solve this problem?
model:
class Oil < ActiveRecord::Base
has_many :other_products_cross_lists, :foreign_key => 'main_id'
class OtherProductsCrossList < ActiveRecord::Base
belongs_to :oil
other_products_cross_lists is an association on your Oil model.
You cannot use update_attributes on an Array or ActiveRecord:Relation object.
What you should do is
#oil.other_products_cross_lists.each {|list| list.update_attributes(:cross_value => #oil.model.to_s.gsub(/\s+/, "").upcase)}
for destroying
you can use
#oil.other_products_cross_lists.delete_all
or
#oil.other_products_cross_lists.destroy_all
You should check out the difference between delete_all and destroy_all for clarity.
as the error says other_products_cross_lists is a relation (I assume your model oil has_many other_products_cross_lists).
update_attribute is a method of an instance of a model, not a method of a relation.
I don't really understand, what you want to do with you update_attribute, but if user nested_attributes, then
#oil.update_attributes(params[:oil])
takes care of updating the relation.
Also, if you define your relation beween Oil and OtherProducts as dependend: :destroy Rails handles the removel of dependent records.
In my Rails app I have an #events collection of objects inherited from Event::Base < AR::Base model.
If rendering it like render :partial => 'event', :collection => #events it's possible to give an option :as => :event to change the name of a local variable corresponding to the object inside of the partial. But what to do when the name of the partial is not constant? The aforementioned way (render #events, :as => :event) doesn't work.
PS. There's a solution at blog.obiefernandez.com but it uses the last part of the partial name and this just doesn't fit for me.
I haven't tried this myself to verify, but this might work:
class Event < ActiveRecord::Base
def to_partial_path
# assuming that you need different partials based on an attribute "variety"
"events/#{variety}"
end
end
You may also need to use the :partial key, eg: render :partial => #events, :as => :event instead of render #events, :as => :event
EDIT: This only works in Rails 3.2+ ... see Obie's writeup on the topic.
I've got two models: Book and ReadingList. A ReadingList has_and_belongs_to_many Books. On the BooksController#show page, I'd like to have a select list that shows all the reading lists, with a button to add the current book to the selected reading list.
Presumably this should go to the ReadingListController#update action, but I can't specify this as the form's URL, because I won't know which ReadingList to send to at the time the form is created. I could hack it with JavaScript, but I'd rather not rely on that.
Would it be better to have a custom action in the BooksController that accepts a reading list id to add the book to, or can I work the routes so this request ends up getting to the ReadingListController#update action?
I suggest that you have a resource which is a ReadingListEntry that represents a book in a reading list. Then you can simply POST to that resource to add it. There doesn't actually need to be a model behind it, you can manipulate the reading list directly.
Obviously this is something that could easily be achieved by using Ajax to submit the form, but in the case where JavaScript is disabled / unavailable, your best option is to have a custom action in the BooksController that adds it to the required reading list.
You could combine both by having the form pointing to the action in the BooksController, but having an onsubmit handler that posts to the ReadingList controller via Ajax.
I would create a custom action and route such that you can provide a book_id and list_id and form the relation.
Assuming you're using restful routes
resources :books do
post '/lists/:list_id/subscribe' => 'lists#subscribe', :as => :subscribe
end
def subscribe
#list = List.find params[:list_id]
#book = Book.find params[:book_id]
#list << #book
end
Now you can use button_to with or without ajax.
Perhaps a has_many :through relationship would be better? I like Anthony's idea of a ReadingListEntry resource - perhaps put a model behind this giving you:
# models/book.rb
has_many :reading_list_entries
has_many :reading_lists, :through => :reading_list_entries
I think here you are changing the Book, not the ReadingList. Therefore you should PUT to the BooksController#update resource with a new list_id attribute.
# in views/books/show.html.erb
<%= form_for #book, :url => book_path(#book) do |f| =>
<%= f.select :list, ReadingList.all.map { |l| [l.name, l.id] } =>
<%= submit_tag "Change" =>
<% end %>
# in controllers/books_controller.rb
# params[:book][:list_id] => 123
def update
#book = Book.find(params[:id])
#book.update_attributes(params[:book])
end
# config/routes.rb
resources :books
resources :lists do
resources :books
end
If you wanted a Book to belong to more than one ReadingList you'd need a has_and_belongs_to_many relationship instead
I have a Parent model which has Children. If all the Children of a certain Parent are deleted, I'd like to automatically delete the Parent as well.
In a non-AJAX scenario, in the ChildrenController I would do:
#parent = #child.parent
#child.destroy
if #parent.children.empty?
redirect_to :action => :destroy,
:controller => :parents,
:id => #parent.id
end
But this is impossible when the request is XHR. The redirect causes a GET request.
The only way I can think of to do this with AJAX is add logic to the response RJS, causing it to create a link_to_remote element, "click" it, and then remove it. It seems ugly. Is there a better way?
Clarification
When I use the term redirect, I do not mean an HTTP redirect. What I mean is that instead of returning the RJS associated with destroying Child, I want to perform destroy on Parent and return the RJS associated with destroying Parent.
I would listen to what nathanvda says, but you can do it via ruby syntax (and you don't need erb scriptlets in rjs):
if #parent.children.empty?
page.redirect_to(url_for :action => :destroy,
:controller => :parents,
:id => #parent.id)
else
.. do your normall stuff here ..
end
A better approach to destroying the parent through a redirect is doing it in an after_hook. Not only you don't have to tell your user's browser to make another request, you also don't need to keep track of everywhere in the code where you delete children so you don't end up with hanging parents.
class Parent < ActiveRecord::Base
# also worth getting the dependent destroy, so you don't have hanging children
has_many :chilren, :dependent => :destroy
end
class Child < ActiveRecord::Base
after_destroy { parent.destroy if parent.children.empty? }
end
Then you can just handle however you prefer what to show the user when that happens, like redirecting the user to '/parents'.
I would guess you could set the window.location.href in your rjs, something like
<% if #parent.children.empty? %>
window.location.href='<%= url_for :action => :destroy,
:controller => :parents,
:id => #parent.id %>'
<% else %>
.. do your normall stuff here ..
<% end %>
assuming you render javascript. Not sure if it is completely correct, but hope you get the idea.
[EDIT: added controller code]
TO make it clearer, your controller would look as follows
#parent = #child.parent
#child.destroy
if #parent.children.empty?
render :redirect
end
I have a model 'User', it's a restful resource, and has the default methods like 'index, show, new, create' and others.
Now, I want to define a new action 'current_user', to show the information of current logged-in user, which is different from 'show'.
When I use:
link_to current_user.name, :controller=>'users', :action=>'current_user'
The generated url is http://localhost:3000/users/current_user, and error message is:
Couldn't find User with ID=current_user
Shall I have to modify the routes.rb? What should I do?
I have searched for some articles, and still have no idea.
Add
map.resources :users, :collection => {:current => :get}
Then, I use:
link_to 'current', current_users_path()
The generated url is:
http://localhost:3000/users/current
Now, everything is OK. Is this the best solution?
See my comment on the other answer for an explanation
map.current_user "users/current", :controller => :users, :action => :current
View:
link_to 'current', current_user_path
I would not add a new action for this. I would check the id passed to the show method.
class UsersController
def show
return show_current_user if params[:id] == "current"
# regular show code
end
private
def show_current_user
end
end
In the view use :current as the user id while generating path.
user_path(:current)