I've been getting the following error when I hit this destroy method in a my User controller.
AbstractController::DoubleRenderError (Render and/or redirect were called multiple times in this action.
Please note that you may only
call render OR redirect, and at most once per action. Also note that
neither redirect nor render terminate execution of the action, so if
you want to exit an action after redirecting, you need to do something
like "redirect_to(...) and return".):
It's a strange one, because I honestly am only responding once to the call.
Here's my action:
def destroy
user = User.find(params[:id])
if user.has_role_for? current_client
# then we remove the role
user.has_no_roles_for! current_client
# was that the users only role?
if user.roles.count == 0
user.destroy
end
respond_with head :ok
else
respond_with({:error=>'unauthorised'}, :status => :forbidden)
end
end
Any ideas?
Try adding " and return" after the respond_with lines:
respond_with head :ok and return
respond_with({:error=>'unauthorised'}, :status => :forbidden) and return
head(:ok) doesn't return something you can respond_with. head :ok renders a 200 with no body. respond_with renders via the responder some representation of the object you passed into it. head calls render, respond_with calls render, hence the double render error.
You should change that line to just head :ok.
Related
I am trying to render all the record of articles having true bookmarked. but it is showing this error :
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
Here is my code:
def index
# Also we are showing all showcased articles
if params[:bookmarked]=="true"
#favourite=Favourite.all
#favourite.map do |f|
article_id = f.item_id
#article = Article.find_by_id(article_id)
render :json => #article
end
end
end
I have an update method which is using save and delete methods having their own render calls. Below is the code:
Update Method:
def update_book
self.delete_book
params[:Name] = params[:NewName]
self.save_book
end
Delete Method:
def delete_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
head 200
end
Save Method:
def save_book
# Do something
rescue BookDoesNotExist => exception
render status: 404
return
end
rescue BookAlreadyExist => exception
render status: 409
return
end
head 200
end
When I run this with some input the system throws "AbstractController::DoubleRenderError" error along with some message "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like "redirect_to(...) and return"
I understand it is because the is throwing exception in both delete and save method with the input I am passing. How do I handle this scenario?
Doing some research I learnt I need to use redirect. So I understand that when my delete method throws exception/renders I should return back and should not call the save method.
Here is what I tried with redirect_to:
Update Method:
def update_book
redirect_to action: self.delete_book and return if status=404 end
params[:Name] = params[:NewName]
self.save_book
end
Am I doing it right or is there any other best way to handle redirect?
When your delete_book or save_book methods receive an exception they will render something and then return, this is correct.
But the return will give execution back to the caller method, which is update_book. This caller method have no idea of the exception that occurred and therefore will do its job and render what it has to render.
To fix, your delete_book or save_book methods need to return a success status to the caller, like this:
def delete_book
# ... Do something
rescue BookDoesNotExist => exception
render status: 404
return false
end
head 200
true
end
Then your caller method shall check for this status:
def update_book
return unless self.delete_book
params[:Name] = params[:NewName]
return unless self.save_book
end
An alternative that will keep your code clear from those heavy exception testing would be to use your controller'srescue_from.
In your ApplicationController:
class ApplicationController < ActionController::Base
rescue_from BookDoesNotExist, with: :redirect_book_not_existing
private
def redirect_book_not_existing
render status: 404
end
end
From now on, very time your controllers receive a BookDoesNotExist exception, the execution will stop and the render status: 404 line will be executed. Off course you must not rescue the exceptions in the other method, like this:
def delete_book
# Do something
head 200
end
Your code will be much cleaner
I have this following code in my controller, the error trace to this piece of code for the error DoubleRenderError.
def build_list(query_string)
tasks = query_tasks(query_string)
#schema2tasklist = schema2tasklist(tasks)
respond_to do |format|
format.html { render :file => "#{Rails.root}/app/views/tasks/list.html.erb" }
end
end
I'm confused where to put the "and return" statement so that only one render is being called at a time.
my list.html.erb also have a render call. I have tried to return from there as well but it didn't help.
These kind of errors happens when you try to render or redirect multiple times inside a method, check the followings
1) if your build_list calling from another method and does it has a redirect too.
def another_method
#code
build_list(query_string)
redirect_to root_url
end
2) does your methods, query_tasks and schema2tasklist has define redirects/renders inside them
In rails three I have the following code for my destroy action in a photos controller
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
respond_to do |format|
if request.xhr?
format.js
else
format.html {redirect_to photos_path}
end
end
else
flash[:alert] = t('.photo.error_deleting')
if request.xhr?
redirect_to(photos_url)
else
redirect_to(photo_path #photo)
end
end
end
The goal is essentially to redirect to the index page if this is called from a standard link and render destroy.js if called from a remote link.
This works but I was wondering if there is a cleaner way of doing this in rails 3. Possibly using the respond_with operator?
Thanks
This should work for you:
respond_to :html, :js
def destroy
#photo = Photo.find(params[:id])
if #photo.destroy
flash[:notice] = t('photo.deleted')
else
flash[:alert] = t('.photo.error_deleting')
end
respond_with(#photo)
end
There is a good blog post about it here:
http://ryandaigle.com/articles/2009/8/10/what-s-new-in-edge-rails-default-restful-rendering
Here's a quote from the post about the logic:
If the :html format was requested:
If it was a GET request, invoke render (which will display the view
template for the current action)
If it was a POST request and the resource has validation errors, render
:new (so the user can fix their
errors)
If it was a PUT request and the resource has validation errors, render
:edit (so the user can fix their
errors)
Else, redirect to the resource location (i.e. user_url)
If another format was requested, (i.e. :xml or :json)
If it was a GET request, invoke the :to_format method on the resource and
send that back
If the resource has validation errors, send back the errors in the
requested format with the
:unprocessable_entity status code
If it was a POST request, invoke the :to_format method on the resource and
send that back with the :created
status and the :location of the new
created resource
Else, send back the :ok response with no body
A little more on the to_format part from the documentation:
First we try to render a template, if
the template is not available, we
verify if the resource responds to
:to_format and display it.
There is also a Railscast about it:
http://railscasts.com/episodes/224-controllers-in-rails-3
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
render :action => show
end
render :template => "fix_user_errors"
end
The previous code will generate an error (because render is called twice) in the case where update_attributes succeeds.
The act of calling render (and redirect_to) should somehow terminate the processing of an action?But its not why?
render doesn't actually terminate the processing of the action.
You need to add an else to your if statement:
if #user.update_attributes(params[:user])
render :action => show
else
render :template => "fix_user_errors"
end
I think you can also use "return render ..." which will return immediately from the action method but this might have unintended consequences.
No, calling render does not end your action. render is a method to tell your controller which template is going to render if you do not want the default one, not to actually tell it you are done processing.
You might want to call return after you call render, if you are actually done.
Also, calling redirect (I am guessing this, I am not sure) actually sends a Location header to your browser, but does not end your action. You need to return or branch too after calling it, so you do not call it twice.
You can use the else, doesnt really has sense to render an action and render another. I would write like this:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_to :action => show
else
render :template => "fix_user_errors" #probably you want to render the edit template here.
end
end