This question already has answers here:
ActiveModel::ForbiddenAttributesError when creating new user
(8 answers)
Closed 6 years ago.
I'm attempting to make a blog post on my web-app updatable from the browser, but when I click update in the edit for, I get this error:
ActiveModel::ForbiddenAttributesError
error in pots_controller line 33:
if #post.update_attributes(params[:post])
this is my edit.html.erb code:
<h1>Edit Post</h1>
<%= form_for #post do |f| %>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %><br />
</p>
<p>
<%= f.label :body %><br />
<%= f.text_field :body %><br />
</p>
<p>
<%= f.select :category_id, Category.all.collect {|x| [x.name, x.id]}, {:include_blank => "Select One"} %><br />
</p>
<p>
<%= f.submit "Update post" %>
</p>
<% end %>
<%= link_to "Go Back", post_path %>
this is my posts_controller.rb code:
class PostsController < ApplicationController
def index
#posts = Post.find(4,5)
end
def show
#post = Post.find(params[:id])
end
def new
#post = Post.new
#category = Category.all
end
def create
#post = Post.new(params[:post])
if #post.save
redirect_to posts_path, :notice => "Your post has been saved"
else
render "new"
end
end
def edit
#post = Post.find(params[:id])
end
def update
#post = Post.find(params[:id])
if #post.update_attributes(params[:post])
redirect_to post_path, :notice => "Your post has been updated"
else
render "edit"
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
redirect_to posts_path, :notice => "Your post has been deleted"
end
end
hope and thank anyone can help. Best, M
Strong Parameters
Rails uses a security mechanism called Strong Parameters by default. Its purpose is to ensure that only certain fields can be updated via a user submitted form.
#post.update_attributes(params[:post]) is an old-style syntax which does not work with strong parameters.
The updated convention is as follows
class PostsController
def update
# ...
#post.update(post_params) # instead of passing params[:post] directly, you pass it a strong parameters whitelist (see below)
end
def post_params
# we construct a strong parameters whitelist below
# require(:post) means that the `params` hash MUST contain a :post key
# permit(:title, :body, ...) = here we enumerate the attributes which we will accept from the form parameters; it acts as a whitelist
params.require(:post).permit(:title, :body, ...)
end
end
If you use rails g scaffold you can see an example of a controller which uses strong parameters.
DON'T DO THIS: To disable using strong parameters by default, you can set the following config value
config.active_record.whitelist_attributes = false
I included this for completeness' sake, however you should not do this as it will unnecessarily introduce security vulnerabilities to your code.
Additional Resources
ActiveModel::ForbiddenAttributesError when creating new user
http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
https://github.com/rails/strong_parameters
Related
the update method doesn't update the record, it creates a new one.
how can I change the code to do the proper job?
here the controller bit:
def edit
#post = Post.find(params[:id])
end
def update
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
the edit view:
= form_for :post, url: posts_path do |f|
%p
= f.label :title
= f.text_field :title
%p
= f.label :content
= f.text_area :content
%p
= f.submit
Change your form tag:
= form_for #post do |f|
It isn't using the variable you set up in your controller so it doesn't know it isn't a new object.
You also shouldn't need to specify the url, so long as you have the resource defined in your routes file.
I've started learning Ruby on Rails.
Iam getting below error: ActiveModel::ForbiddenAttributesError in PostsController#create
Here is the code:
posts_controller.rb
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find params[:id]
end
def new
#post = Post.new
end
def create
#post = Post.create(params[:post]) // Its showing me error here
if #post.save
redirect_to posts_path notice: "Your Post was saved"
else
render "new"
end
end
end
new.html.erb
<h1> Add a New Post </h1>
<%= form_for #post do |f| %>
<p>
<%= f.label :title %><b/>
<%= f.text_field :title %><b/>
</p>
<p>
<%= f.label :content %><b/>
<%= f.text_area :content %><b/>
</p>
<%= f.submit "Add a New Post" %>
<% end %>
post.rb
class Post < ActiveRecord::Base
validates_presence_of :title, :content
end
Please tell me what went wrong. BTW, iam using Rails4
In rails4 there is strong parameters so you can't do this:
#post = Post.create(params[:post])
You need
#post = Post.create(post_params)
Where post_params is a method in your controller:
private
def post_params
params.require(:post).permit!
end
permit! will permit anything. You can limit it though to what you want to allow for example params.require(:post).permit(:title, :content)
You have to use strong params for rails 4 to solve this problem.
Docs: http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters
def post_params
params.require(:post).permit(:title, :content)
end
and in create action use: Post.create(post_params)
You have to use strong params always when you create or update a record.
So, I'm running into a fairly simple problem, where I cannot enter some simple form values into my SQLite DB (Rails).
Interestingly, the code doesn't fail - I submit the form, and am redirected successfully to the correct URL - and a record IS inserted into the DB, but the columns "name, type and user_id" are not filled with anything. To clarify, the columns are blank, for that new record.
If I comment out the code in the "create" and simply spit out the params (render plain: params[:plan].inspect) I do see all the correct parameters filled out, so I have a feeling there must be something wrong in the line:
#plan = Plan.new(params[:plan])
I'm really stuck here, any guidance would be much appreciated!
The create form
<h1> Create a new plan </h1>
<%= form_for :plan, url: plans_path do |f| %>
<p>
<%= f.label :name %><br>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :type %><br>
<%= f.text_field :type %>
</p>
<%= f.hidden_field :user_id, :value => current_user.id %>
<p>
<%= f.submit %>
</p>
<% end %>
plans_controller.rb
class PlansController < ApplicationController
def index
#plans = Plan.all
end
def show
#plan = Plan.find(params[:id])
end
def new
#plan = Plan.new
end
def create
#render plain: params[:plan].inspect
params.permit!
#plan = Plan.new(params[:plan])
if #plan.save
redirect_to #plan
else
redirect_to dashboard_path, :notice => "Plan NOT Created!"
end
end
end
The Model
class Plan < ActiveRecord::Base
end
Edit the plans_controller.rb:-
def create
#render plain: params[:plan].inspect
#plan = Plan.new(plan_params)
if #plan.save
redirect_to #plan
else
redirect_to dashboard_path, :notice => "Plan NOT Created!"
end
end
private
def plan_params
params.require(:plan).permit(:name,:type,:user_id)
end
Change the field name type as :-
rails g migration rename_fields_in_plans
class RenameFieldsInPlans < ActiveRecord::Migration
def change
rename_column :plans, :type, :plan_type
end
end
Then run the command:-
rake db:migrate
I have a form that allows the user to Post in the a group Show method. Upon posting, I want to redirect to the same page showing the new post. I'm using the following, but I get the error below. I'm not sure why #group is nil, because I've defined it in the show of my group controller.
No route matches {:id=>nil} missing required keys: [:id]
for
redirect_to group_path(#group)
<%=form_for([#post]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class = "field">
<%= f.label :event_name %>
<%= f.collection_select(:event_id, #events, :id, :title) %>
</div>
<div class = "field">
<%= f.text_area :comment, placeholder: "New Post..." %>
</div>
<%= f.hidden_field :user_id, value: current_user.id %>
<%=f.submit "Submit", class: "btn btn-large btn-primary" %>
<%end%>
class PostsController < ApplicationController
def create
if #post = Post.create(post_params)
flash[:success] = "Post Created!"
redirect_to group_path(#group)
else
redirect_to group_url
flash[:alert] = "Sorry - Post not created."
end
end
end
def show
#event = #group.events.build
#post = Post.new
#events = #group.events.includes(:posts)
#group = Group.find(params[:id])
end
In your create action you attempt to use the #group instance variable. You haven't defined it in the create action so you'll need to create it there if you want to use it. Since the call to create is in a separate request cycle the instance variables you defined in the show action are not available.
Update:
To get the group if you have an event_id and event belongs_to :group you would do:
event = Event.find(event_id)
#group = event.group
Set #group in create action. You have not assigned any value to #group there which is why you are getting error.
EDIT
As per your comment A Group has_many events so you can find the group as below:
#group = Event.find(params[:event_id]).group
So I have a fairly typical blog application with posts and comments.
Each comment belongs to one post
A post can have many comments.
Basically I want to add a form for comments to the show action for posts, without having post_id under attr_accessible in the comment model.
In my posts controller I have:
def show
#post = Post.find(params[:id])
#poster = "#{current_user.name} #{current_user.surname} (#{current_user.email})"
#comment = #post.comments.build( poster: #poster )
end
I'm not entirely sure what I should be doing in the comments controller (I'm not confident that the code above is right either if I'm honest). At the moment I have:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:post])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
My routes:
resources :comments
resources :posts do
resources :comments
end
and finally the form:
<%= form_for #post.comments.build do |f| %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.hidden_field :post_id, value: #post.id %>
<%= f.submit "Post" %>
<% end %>
The problem here is that I have no way of passing my post_id from the show action of the posts controller to the create action of the comments controller. Any help is much appreciated. Thank you in advance!
Your posts controller looks fine... but assuming your routes looks like
resources :posts do
resources :comments
end
then your CommentsController#create should/could look like:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
And your form:
<%= form_for [#post, #comment] do |f| %>
<%= f.hidden_field :poster, value: #poster %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.submit "Post" %>
<% end %>
I will assume that your post model has_many comments and comment belongs_to post
than you in your routes file you can do something like this
resources :posts do
resources :comments
end
this will give you a url schem such as
/posts/:post_id/comments , allowing you to always have post_id of the comment parrent
The URL for your show post should be like post/show/(:id).
Now, in the comment form, you can place a hidden field, with value of params[:id].
hidden_field(:post_id, :value => params[:id])
When you submit your form, you can get the value of post_id using the hidden field.
def create
#comment = Comment.new(params[:comment])
#comment.post_id = params[:post_id]
if #comment.save
flash[:notice] = 'Comment posted.'
redirect_to post_path(#comment.post_id)
else
flash[:notice] = "Error!"
redirect_to post_path(#comment.post_id)
end
end