I tend to not need the mass-assignment feature in my production code. (In my test code, I use it a lot, but in those cases I do want to set arbitrary columns.)
So if, in my production code, I simply avoid these forms:
Article.new(params[:article]) # or create
article.attributes = params[:article]
article.update_attributes(params[:article])
and instead always manually enumerate all the attributes, like so:
Article.new(:title => params[:article][:title], :body => params[:article][:body], ...)
am I save from mass assignment security issues (even without using attr_accessible
/attr_protected
)?
Edit: The reason I'm not just disabling mass assignment is, I'd like to be able to write Article.create!(:blog_id => @blog.id, ...)
, where blog_id is an "unsave" attribute.
Yes, using the 2nd method, you're safe from users assigning to other attributes.
This is a DRYer way to write it, though:
Article.new(params[:article].slice(:title, :body))
-or-
def article_params
params[:article].slice(:title, :body)
end
Article.new(article_params) # or create
article.attributes = article_params
article.update_attributes(article_params)
Add this at the end of config/environments/production.rb
:
ActiveRecord::Base.send(:attr_accessible, nil)
I could not get John Douthat's method working for multiple parameters, so I came up with the following alternative (taken from my CommentsController):
def set_params
@comment.parent_id = params[:blog_comment][:parent_id]
@comment.ip_address = request.remote_ip
@comment.commentator = current_user.username || "anonymous"
@comment.name = params[:blog_comment][:name]
@comment.email = params[:blog_comment][:email]
@comment.url = params[:blog_comment][:url]
end
def create
@comment = @post.comments.build(params[:blog_comment])
set_params
if @comment.save
...
end
def update
@comment = Blog::Comment.find(params[:id])
set_params
if @comment.update_attributes(params[:blog_comment])
...
end