Francisco
Francisco

Reputation: 13

ForbiddenAttributesError error when building from a has_many relationship

New updates

Moved params permit responsability from model to controller and used comment_attributes instead of comments as @vinodadhikary pointed me

Using better_errors REPL, I traced the problem down to sanitize_for_mass_assignment method. When doing attributes.permitted? it returns false. But doing attributes.permit(:article_id, :name, :email, :body) returns me exactly que entry parameters!:

>> attributes
=> {"name"=>"Commenter", "email"=>"[email protected]", "body"=>"Here is the comment >> body!! :D"}
>> attributes.permit(:article_id, :name, :email, :body)
=> {"name"=>"Commenter", "email"=>"[email protected]", "body"=>"Here is the comment body!! :D"}
>> attributes.permitted?
=> false

Context and code

Trying to get in touch with Rails 4, I encountered a problem with (I think) strong parameters use.

I have an Article class which can have many Comments. When creating a new comment doing:

@comment = @article.comments.build(params[:comment])

I get the following error (pointing this line):

ActiveModel::ForbiddenAttributesError at /articles/1/comments

The models are the following:

class Article < ActiveRecord::Base
  validates_presence_of :title, :content
  validates_uniqueness_of :title

  has_many  :comments, :dependent => :destroy
  accepts_nested_attributes_for :comments
end

Comments:

class Comment < ActiveRecord::Base
  belongs_to :article

 validates_presence_of :article_id, :author, :body, :content
end

Article controller have this in the private section:

def article_params
  params.require(:article).permit(:title, :content, comments_attributes: [:article_id, :name, :email, :body])
end

Comments controller code is:

def create
  @article = Article.find(params[:article_id])
  @comment = @article.comments.build(params[:comment]) # <--- It fails here

  respond_to do |format|
    if @comment.save
      format.html { redirect_to @comment, notice: 'Comment was successfully created.' }
      format.json { render action: 'show', status: :created, location: @comment }
    else
      format.html { render action: 'new' }
      format.json { render json: @comment.errors, status: :unprocessable_entity }
    end
  end
end

Upvotes: 1

Views: 8363

Answers (2)

Danish Rasheed
Danish Rasheed

Reputation: 55

permit params method name should be same as model/controller e.g if model name is "recent_post" then permit method name should be

def recent_post_params .............. end

Upvotes: 0

vee
vee

Reputation: 38645

The methods article_params and comment_params that you have in the models belong in their respective controllers not in models. The idea is to filter the parameters passed to the model in the controller rather than in the model. Take a read on http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html, on how to allow attributes for nested attributes.

You models should be as follows:

# Articles.rb
class Article < ActiveRecord::Base
  validates_presence_of :title, :content
  validates_uniqueness_of :title

  has_many  :comments, :dependent => :destroy
  accepts_nested_attributes_for :comments
end

# Comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article

  validates_presence_of :article_id, :author, :body, :content
end

Then move the strong parameters to Articles Controller as follows:

#ArticlesController.rb
def create
  @article = Article.find(params[:article_id])
  @comment = @article.comments.build(params[:comment])

  respond_to do |format|
    if @comment.save
      format.html { redirect_to @comment, notice: 'Comment was successfully created.' }
      format.json { render action: 'show', status: :created, location: @comment }
    else
      format.html { render action: 'new' }
      format.json { render json: @comment.errors, status: :unprocessable_entity }
    end
  end
end

private 
    def article_params
        params.require(:article).permit(:title, :content, comments_attributes: [:article_id, :author, :email, :body, :content])
    end

Upvotes: 3

Related Questions