When I try to delete #article, I am getting this error: ActiveRecord::RecordNotFound in ArticlesController#destroy, Couldn't find Article with id=1... But when I go back on Index page, #article is deleted.
What should I do?
Destroy method:
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to 'root'
end
Link for delete(/show.html.erb):
<%= link_to "delete", article_path(#article), method: :delete %>
If you need more files, I will upload them..
Try using:
redirect_to :root if #article.destroy
If you want to avoid errors when hitting the destroy action for a record that is already deleted you could use the following:
def destroy
if article = Article.find_by(id: params[:id])
article.destroy
end
redirect_to root_path
end
find_by returns nil if it doesn't find the record while find throws an exception. nil is considered 'falsey' by ruby which will bypass the body of the if when the record is not found.
It is also best to use a path helper to avoid having 'magic' strings in your code that you have to remember to update if you decide to change your routes file.
Simply Just change a little in the redirect_to line
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to :action => :index #this will redirect to article index page if delete action was successful
end
Like #bhanua1 say
try using redirect to :root if #article.destroy
Related
I am writing an app in RoR, and I was wondering if following if statement should be put there:
class ArticlesController < ApplicationController
before_action :set_user, only: %i[destroy edit]
...
def destroy
if #article.destroy
flash[:success] = 'Article deleted'
redirect_to articles_path
else
flash[:error] = 'Failed to delete the article'
redirect_to article_path(#article)
end
end
...
private
def set_user
#article = Article.find(params[:id])
if current_user.id == #article.author_id
#author = current_user
else
redirect_to article_path(#article), message: 'You can\'t do that'
end
end
end
Is it possible for "else" branch i destroy method to be called? I am writing specs for that controller and I can't possibly think of the way to test that code.
I used the same approach as with ".save", but there we are dealing with DB validation. If there is no way "else" can be called, I will just delete those lines.
I something goes wrong with searching for the article owner or finding the article by id, error is gonna be present earlier, in "set_user" method.
I was reading rails guides - http://guides.rubyonrails.org/
I am not able to understand the syntax: redirect_to #article inside the method :
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
and also I am not able to comprehend the syntax- url: article_path(#article)
inside the defination of form
<%= form_for :article, url: article_path(#article), method: :patch do
Here we are creating a Article using create method. The block is first creating a article using the article_params send from the form and if the newly created Article is saved to database then we are redirected to the article itself else we are redirected to the form showing errors.
When you create any article in RoR platform then ideal tutorial is This guide, when you use Rails form then don't need to mention method because rails form built in some method like get post put/patch & destroy he automatically understood what kind of your request just follow this also this
def create
#article = Article.new(article_params)
#article.save
redirect_to #article
end
private
def article_params
params.require(:article).permit(:title, :text) #-> passing article parameters
end
Have a basic blog (it's actually edgeguide's blog: http://edgeguides.rubyonrails.org/getting_started.html)
Then I integrated Devise into it. So, user can only log in and see their own information.
Now trying to change it somewhat.
I'd like the users to see all content, but only edit and destroy their own only.
Trying to use before_action filter like this:
`before_action :authorize, :only => [:edit, :destroy]`
And this is the authorize method that I wrote:
def authorize
#article = Article.find(params[:id])
if !#article.user_id = current_user.id then
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
end
end
But it doesn't work. Everything acts as normal, and I can delete mine and everyone's else content.
How do I get it that I can destroy ONLY my own content, and not everyone's else?
Not using CanCan, nor do I want to.
Not sure if this is worth including or not, but originally when I had everyone see their own content, that was via create action:
def create
#article = Article.new(article_params)
#article.user_id = current_user.id if current_user
if #article.save
redirect_to #article
else
render 'new'
end
end
You're having several problems
first, look at that :
if !#article.user_id = current_user.id then
You're only using one = instead of == so you are doing an assignation that will evaluate to current_user.id
Also, in your condition, you're only setting a flash message but not doing anything to really prevent the user.
Here's a corrected version :
def authorize
#article = Article.find(params[:id])
unless #article.user_id == current_user.id
flash[:notice] = "You are not the creator of this article, therefore you're not permitted to edit or destroy this article"
redirect_to root_path # or anything you prefer
return false # Important to let rails know that the controller should not be executed
end
end
Hi I am new to rails and I am trying to figure out how to prevent unauthorized access to the update action of a controller.
I know I could have a before_filer that kicks out people that arent logged in and I have redirect_to in the edit action, but I want a way to stop a user from editing an object that does not belong to them.
Ex: A authorized user can simply change a job object in my app, by directly sending a PUT request with any job.id as a parameter and change any field they want.
Here is my controller:
def update
#job = Job.find(params[:id])
#job.update_attributes(params[:job])
redirect_to jobs_path
end
To try and fix this problem I tried to check in the update action if it the user was authorized and if they werent, i would redirect them to the index page.
def update
#job = Job.find(params[:id])
if #job.user.id != current_login
redirect_to jobs_path
end
#job.update_attributes(params[:job])
redirect_to jobs_path
end
But when I try to do this, rails gives me an error saying I can only have one redirect in an action.
Well, the straightforward fix to your immediate problem is to use flow control to make sure that only one redirect_to is ever reached on a single request, as many others have suggested.
However, that's not really how I'd solve your larger problem.
First, there are a lot of existing solutions for managing authorization, such as cancan or rolify. I'd look into those.
Second, I'd use a before_filter to block access, as you suggest. Something like:
before_filter :load_job, :only => [:show, :edit, :update, :delete]
before_filter :require_authorization, :only => [:edit, :update, :delete]
def load_job
#job = Job.find(params[:id])
end
def require_authorization
redirect_to jobs_path unless current_user.can_edit?(#job) # or whatever you want to check
end
The before filters will execute in order, so you'll already have the user & the job available when you check permissions, and can check permissions for that specific job.
def update
#job = Job.find(params[:id])
#job.update_attributes(params[:job]) unless #job.user.id != current_login
redirect_to jobs_path
end
:)
This is probably because after the first redirect the second one could still be executed.
Thus putting the update_attributes and the second redirect into the else path like this should solve the problem:
def update
#job = Job.find(params[:id])
if #job.user.id != current_login
redirect_to jobs_path
else
#job.update_attributes(params[:job])
redirect_to jobs_path
end
end
You can either do redirect_to jobs_path and return or return redirect_to jobs_path.
Try the following:
def update
#job = Job.find(params[:id])
if #job.user.id != current_login
redirect_to jobs_path and return
end
#job.update_attributes(params[:job])
redirect_to jobs_path and return
end
Use an Else Clause
The problem is that the redirect_to method doesn't end the current method; it just tells the controller to set some headers. In order to prevent this problem, you need to make sure that control doesn't "fall through" to the second redirect. One way to do this would be to put your alternative path into an else clause. For example:
def update
#job = Job.find(params[:id])
if #job.user.id != current_login
redirect_to jobs_path
else
#job.update_attributes(params[:job])
redirect_to jobs_path
end
end
I have been trying to get to grips with jQuery and been following a railscast on adding an Ajax add review form, which works fine but I would now like to add into it the ability for a review to belong to a user as well as a venue.
Reviews controller
def create
#review = Review.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
views\reviews\create.js.erb
$("#new_review").before('<div id="flash_notice"><%= escape_javascript(flash.delete(:notice)) %></div>');
$("#reviews_count").html("<%= pluralize(#review.venue.reviews.count, 'Review') %>");
$("#reviews").append("<%= escape_javascript(render(:partial => #review)) %>");
$("#new_review")[0].reset();
I have tried changing the controller to:
def create
#review = #current_user.reviews.create!(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end
but it just wont submit, with no errors.
I think I have the models set correctly with belongs_to and has_many, I think this is a controller issue I'll add other code bits if needed.
Development log
NoMethodError (undefined method `reviews' for nil:NilClass):
app/controllers/reviews_controller.rb:14:in `create'
Thanks for any help!
It appears that your error is residing with #current_user. According to your development log, #current_user is nil when you call #current_user.reviews on it. I would say track down where this #current_user instance variable is being set and find out why it is nil. Now, what kind of authentication are you using? Most authentication plugins, especially those used by Ryan Bates of the Railscasts you mentioned, use a local variable, say just current_user, as the means to access the currently signed in user. I know I do in all my code.
So, rewrite the line as
#review = current_user.reviews.create!(params[:review])
and see if that works. If it doesn't, change it back and then track down where this #current_user is being set. Chances are good it is being set in a before_filter :method_name at the beginning of your controller.
Calling create! (with exclamation mark) will throw an exception and thus abort your create action if saving fails. Check your log/development.log for these exceptions.
Use build instead of create and lose the exclamation mark.
def create
#review = #current_user.reviews.build(params[:review])
#review.venue = #venue
if #review.save
flash[:notice] = 'Thank you for reviewing this venue!'
respond_to do |format|
format.html { redirect_to venue_path(#venue) }
format.js
end
else
render :action => :new
end
end