70

In Rails 3, it was possible to insert an attribute into params like so:

params[:post][:user_id] = current_user.id

I'm attempting to do something similar in Rails 4, but having no luck:

post_params[:user_id] = current_user.id

. . . .


private

  def post_params
    params.require(:post).permit(:user_id)
  end

Rails is ignoring this insertion. It doesn't throw any errors, it just quietly fails.

nullnullnull
  • 8,039
  • 12
  • 55
  • 107

4 Answers4

149

Found the answer here. Rather than inserting the attribute from within the controller action, you can insert it into the params definition with a merge. To expand upon my previous example:

private

  def post_params
    params.require(:post).permit(:some_attribute).merge(user_id: current_user.id)
  end
Dave Powers
  • 2,051
  • 2
  • 30
  • 34
nullnullnull
  • 8,039
  • 12
  • 55
  • 107
  • @marflar you used `deep_merge` within the params constructor? Or elsewhere in your controller? – Brian Jordan Jun 18 '14 at 00:01
  • This is how I do it also but I keep thinking there must be a way to DRY this up a bit. All of my 50+ controllers have a similar .merge statement down in the strict params area. In my case I merge the current_user into updated_by. I merge current_user into created_by only in the create method. – Dan Jul 31 '14 at 16:17
  • @Dan, I guess you use associations, so it should be easier to use something like: `current_user.items.create(item_params)`. I use `merge` only when I have another association to create, i.e. `@comment = @commentable.comments.new(comment_params)` and my `comment_params` method looks like this: `params.require(:comment).permit(:body, :parent_id, :removed).merge(user_id: current_user.id)` – Almir Sarajčić Jan 30 '15 at 17:04
  • Guys, pls take a look at my question and help me out if you can. It closely relates to this one: http://stackoverflow.com/questions/33357501/rails-attribute-changes-in-spite-of-not-being-in-form – Sean Magyar Oct 27 '15 at 00:09
  • If like me you were looking for how to do it with custom value in params you can do ` params.require(:post).permit(:some_attribute).merge(user_id: params[:post][:id])` – eXa Feb 22 '16 at 01:46
39

In addition to @timothycommoner's answer, you can alternatively perform the merge on a per-action basis:

  def create
    @post = Post.new(post_params.merge(user_id: current_user.id))
    # save the object etc
  end

private
  def post_params
    params.require(:post).permit(:some_attribute)
  end
Brian Jordan
  • 2,377
  • 3
  • 21
  • 29
Fellow Stranger
  • 32,129
  • 35
  • 168
  • 232
  • 2
    Hey, how would you do that for a nested resource ? – Mene Nov 11 '15 at 16:57
  • i'm not sure why but @timothycommoner's answer doesn't work for me. only this one does... i even tried `merge!` and that still failed. oh well this reads easier anyway because there's no digging in private methods and it's easier to change in different use cases – james Jan 26 '16 at 16:32
  • @Patient55 I guess you need `deep_merge` as they discussed in the selected answer's comments. – Wit Jun 28 '17 at 05:17
3

As an alternative for this case, you can required pass attribute via scope:

current_user.posts.create(post_params)

BitOfUniverse
  • 5,903
  • 1
  • 34
  • 38
1

If anyone is trying to figure out how to add/edit a nested attribute in a Rails 5 attributes hash, I found this to be the most straight-forward (alternate) approach. Don't bother with merge or deep_merge...it's a pain due to the strong parameters. In this example, I needed to copy the group_id and vendor_id to the associated invoice (nested parameters) prior to saving.

def create
  my_params = order_params
  @order = Order.new
  @order.attributes = my_params
  @order.invoice.group_id = my_params[:group_id]
  @order.invoice.vendor_id = my_params[:vendor_id]
  @order.save
end

private

# Permit like normal
def order_params
  params.require(:order).permit([:vendor_id, :group_id, :amount, :shipping,:invoice_attributes => [:invoice_number, :invoice_date, :due_date, :vendor_id, :group_id]])
end
Hollie B.
  • 199
  • 2
  • 7