Im trying to redirect someone after they click a button and I keep getting the following error:
ActiveModel::ForbiddenAttributesError
Extracted source (around line #20):
It is throwing the error on the #post = Post.new line.
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => "Your post was saved"
I am very new to Ruby and at the moment I am very confused of what this means. I am just following a tutorial and mine isnt working. If anyone could help that would be awesome :D
#post = Post.new(params[:post])
... is no longer used in the latest versions of rails. The problem is that it provided weak security. Someone who was updating their user profile (for example) could theoretically insert an attribute like "administrator: true" to change themselves into an administrator (if that's how admin flag is stored)
Strong parameters now require that you explicity specify which attributes you want to allow to be entered.
So nowadays we do...
#post = Post.new(post_params)
And we have a method later in the controller that specifies the permitted attributes, and looks like...
def post_params
params.require(:post).permit(:title, :body)
end
While I don't have quite enough of your code to specifically answer the question, I can probably get pretty close (minus some column/attribute naming). With strong_params now the standard for Rails applications, you'd probably be looking to do something more like:
def create
#post = Post.new(post_params)
if #post.save
redirect_to posts_path, :notice => "Your post was saved"
else
#other stuff here
end
end
private
def post_params
params.require(:post).permit(:content, ....etc) #I took a guess at the attributes you are passing through your params on the create.
end
For a little extra easy-reading on the history/reason: http://blog.8thlight.com/will-warner/2014/04/05/strong-parameters-in-rails.html
Let me know if you'd like any additional clarification.
Related
I have creating a web application using Ruby on Rails but now I'm troubling a issue which is post updating, I'm trying to solve this issue which followed this also this. Below is my code:
def update
#edit = Post.find(params[:post_id])
params.permit!
if #edit.update_attributes(params[:post])
redirect_to home_path
flash[:notice] = "Your post updated"
else
flash[:notice] = "Wrong"
end
end
When I use this code then showing
When assigning attributes, you must pass a hash as an argument.
After that
def update
#edit = Post.find(params[:post_id])
params.permit!
if #edit.update_attributes(update_params)
redirect_to home_path
flash[:notice] = "Your post updated"
else
flash[:notice] = "Wrong"
end
end
private
def update_params
params.require(:post).permit(:title, :details, :summery)
end
When I use this then showing
param is missing or the value is empty: post
Where is my actual fault?
It's your form that is causing this error. It means that you aren't passing the values nested inside post. If you view your params (raise params.inspect) you'll be able to see that post doesn't exist.
If you use form_for (or simple_form or formtastic) this is handled for you, but if you are using form_tag or manually creating a form with HTML, or submitting data via ajax you need to add this.
In ajax, you can add by serializing the data before submitting - in a form_tag you can do it this way:
form_tag post_path(#post)
I am a beginner of Rails. I am learning rails with the book 'Beginning Rails 4' now. I want to ask you about 'parameter' passed to params method. The following is one of typical rails controllers.
class CommentsController < ApplicationController
before_action :load_article
def create
#comment = #article.comments.new(comment_params)
if #comment.save
redirect_to #article, notice: 'Thanks for your comment'
else
redirect_to #article, alert: 'Unable to add comment'
end
end
def destroy
#comment = #article.comments.find(params[:id])
#comment.destroy
redirect_to #article, notice: 'Comment Deleted'
end
private
def load_article
#article = Article.find(params[:article_id])
end
def comment_params
params.require(:comment).permit(:name, :email, :body)
end
end
Yes, this is just a typical comment controller used to create a comment attached to an article. The Comment model 'belongs to' the Article model, and the Article model 'has many' comments.
Take look at the destroy method.
def destroy
#comment = #article.comments.find(params[:id])
-- snip --
end
It finds the comment associated with the article by find(params[:id]). My question is, where on earth does params[:id] come from?
Does it come from URL? Or does rails save params hash automatically whenever any comment record is created? So we can find any comment by find(params[:id])?
The load_article method is similar.
def load_article
#article = Article.find(params[:article_id])
end
It finds an article by params[:article_id]. Where does this params[:article_id] come from? How does rails find an article by this?
params[:id] is meant to be the string that uniquely identifies a (RESTful) resource within your Rails application. It is found in the URL after the resource's name.
For example, for a resource called my_model, a GET request should correspond to a URL like myserver.com/my_model/12345, where 12345 is the params[:id] that identifies that specific instance of my_model. Analogies follow for the other HTTP requests (PUT, DELETE etc) and their RESTful counterparts.
You should read about Rails routing and its interpretation of RESTful architecture if you're still confused about these concepts and terminologies.
params[:id] does come from the URL. When you use resources in your routes file, Rails will automatically generate the standard REST routes for you. In your destroy example, that would usually be a be a request to /comments/:id using the DELETE HTTP method, in which that :id is added to the params hash, i.e. params[:id].
For example I have a controller PostController in RoR 4. and actions in it:
def index
#posts = Post.all.order('created_at DESC')
end
def new
#post = Post.new #second question
end
def create
#post = Post.new(post_params)
post.save
redirect_to #post #first question
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:title, :body)
end
so, the first question is: Why when we write redirect_to #post it automatically redirect to show/id? How rails know, that it must go to show view?
second question: why I must write #post = Post.new in new action? when I comment it, I also can create a Post. What is the deference write it in new action or missing it?
I'm new in rails and there are many magic in it
Q1). This is part of one of main RoR's mantras - convention over configuration. It just assumes, when performing redirect_to #post, you actually want to see the #post resource. The standard REST action of "reading" the resource is show.
But, if - for example - in your view you will do something like
<%= link_to #post.title, #post, method: :delete %>
What will be generated by that will take you to destroy action of #post's resource.
Having conventions like this make you write less code, and - make you happy!
Q2). I'm surprised if what you mentioned worked. It shouldn't, because similar magic is performed in your form_for #post tag in your views. If #post is empty, form_for shouldn't be able to resolve what url to generate, or how to behave in general - depending on Post.new stored in #post it assumes you want to create the resource, and prepare your <form> tag to submit with POST method, but if in #post there is stored already persisted resource, say Post.find(1), it will create the <form> to be submitted with PATCH method.
Again - based on convention, you can write less code, to achieve more.
I strongly advise against omitting something like #post = Post.new. It may lead to hard to identify bugs, even if it worked for you at the moment
Hope it clarifies things!
Good luck!
First questions: it redirects you to the show page using the url_for method (you can read its source if you are curious), remember that #book has an id so it takes that id and includes it in the url of the /books/:id
Second question : you need #post for when you submit an invalid form, then #post will be populated with the errors and displayed on the new page. try to submit an invalid post, you will get complains about a not defined method.
to display the errors of an invalid submission :
<%= form_for :post posts_path do |f| %>
<% if #post.errors.any? %>
See how we used #post above? just to get the errors in it.
When passing a record to redirect_to the url will be generated by calling url_for, which will return a named url for that record. For this it uses the class name for lookup. So passing the Post record will attempt to use the post_path route with the original record as parameter which will resolve in /posts/:id.
First question
Rails magic.
As you pass an ActiveRecord object to redirect, the restful routing interprets this as you wanting to go to the show page of that object.
Plenty of different ways to use redirect_to here.
Second question
Usually #post = Post.new is required as you reference #post in the new action as your forms when constructed with form_for .. useful for validation and dealing with errors.
Technically you could use a form_tag and define Post.new right there in the form. This would mean the #post = Post.new is redundant and works when you comment it out. This way would mean you have no way to pass your objects errors back to the user on the new page though.
I've been following this Rails tutorial:
http://guides.rubyonrails.org/getting_started.html
Section 5.7 tells me that I should expect an ActiveModel::ForbiddenAttributesError
The thing is, I don't get the error. It works without the permit keyword.
My create method looks like this:
def create
#post = Post.new(post_params)
#post.save
redirect_to #post
end
I'm working with Rails 4.0 and Ruby 2.0. Any idea why the strong parameters security function isn't working?
The documentation is actually misleading, you're right.
If you coded your controller as shown in chapter 5.6
def create
#post = Post.new(post_params)
#post.save
redirect_to #post
end
private
def post_params
params.require(:post).permit(:title, :text)
end
you're already permitting the use of the parameters title and text.
The next chapter (5.7) assumes you didn't use the permit-method already.
If you'd change Line 2 to:
#post = Post.new(post_params)
as seen in the screenshot, the error will be thrown. Additionally, the 'fix' in chapter 5.7 doesn't define a new private method post_params as you did, but applies the fix inline.
#post = Post.new(params[:post].permit(:title, :text))
By Any chance do you have this code in your posts_controller:
private
def post_params
params.require(:post).permit(:title, :text)
end
according to the new security measure conventions in rails 4
http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
if you have this , that means you are permitting to use those params to be used in the creation and updation of models.
and as you said you are following this tutorial
http://guides.rubyonrails.org/getting_started.html#saving-data-in-the-controller
you have this code in your controller.
Doh... followed the documentation word for word -- yes, it is misleading.
The permit is already in the post_params method:
def post_params
params.require(:post).permit(:title, :text)
end
A newbie question regarding the ActiveRecord's save method.
If i have this code (as in rails guide):
def create
#post = Post.new(params[:post])
if #post.save
redirect_to #post
else
.....
end
end
The save method returns the new created Post object? How, after the code #post.save, rails know how to substitute the redirect_to #post with the proper post_id (1 or 2 or 3 or ....) to build the link?
It's an OOP concept.
So, after create, the #post is updated with the id, and everything just works.
Take a look at this code.
Hope it helps.
I'm going to explain it by presenting another piece of code that should make things easy to understand
def create
#user = User.new(params[:user])
if #user.save
redirect_to #user
Now :user contains the following information based on the User model: id, name, email, telephone.
So that means we have actually assigned to #user the above hashes.
The redirect_to will retrieve the hashes that is already stored in #user and display the new profile page.
I hope it helped.