Here are a few quick tips to help you sidestep some common issues with accepts_nested_attributes
:
This catches me all the time—be sure to append _attributes
when using fields for a nested object.
Example:
form.fields_for :profile_attributes
params.require(:user).permit(profile_attributes: [:name, :description])
By default, accepts_nested_attributes
will delete an existing object and create a new one if it receives an ID it doesn’t recognize. This behaviour can unintentionally destroy data or break associations.
To ensure existing objects are only updated and not replaced, use update_only: true
:
accepts_nested_attributes_for :profile, update_only: true
Nested objects can also have their own accepts_nested_attributes
. If your User
has an Account
, and the Account
has a Profile
, you can update everything in one go:
<%= form_with model: @user do |form| %>
<%= form.fields_for :account_attributes do |account_fields| %>
<%= account_fields.fields_for :profile_attributes do |profile_fields| %>
<%= profile_fields.text_field :name %>
<% end %>
<% end %>
<%= form.submit %>
<% end %>
And a single controller can process it while still using strong parameters:
class UsersController < ApplicationController
def update
@user = Current.user
@user.update(user_params)
# …
end
def user_params
params.require(:user).permit(
:email_address,
account_attributes: [profile_attributes: [:name]]
)
end
end
This approach keeps your code structured and protects against unwanted parameter injection by leveraging strong parameters at every level.