tfantina
tfantina

Reputation: 815

Rails accepts_nested_attributes_for

I am working on hand-rolling a user authentication system for fun. I've created two different models, a UserAccount and UserCredential the idea being that credentials such as email, username, profile pic, etc are separate from the actual account.

In order to create a UserCredential when a UserAccount is created I am using accepts_nested_attributes_for. I am running into some issues saving the UserCredential when the UserAccount saves.

Migration:

  create_table :user_accounts, id: :uuid do |t|
      t.references :user_credentials
      ...
    end

    add_reference :user_credentials, :user_account, type: :uuid, null: false, foreign_key: true 

user_account.rb

  has_one :user_credential, inverse_of: :user_account, dependent: :destroy
  ...
  accepts_nested_attributes_for :user_credential
  validates_associated :user_credential

user_credential.rb

class UserCredential < ApplicationRecord
  validates :email, uniqueness: true

  belongs_to :user_account, inverse_of: :user_credential
  validates_presence_of :user_account

accounts_controller.rb

  def new
    @user = UserAccount.new
  end

  def create
    @user = UserAccount.create!(user_params)
  end

  private

  def user_params
    params.require(:user_account).permit(user_credentials: [:email])
    # I'm not sure if `user_credential` should be plural here or not?

  end

_form.html.erb

<%= form_with model: @user, local: true do |form| %>
  <%= form.fields_for :user_credentials do |u| %>
    <div class="form-control">
      <%= u.label :email %>
      <%= u.email_field :email %>
    </div>
  <% end %>
   ...
<% end %>

Two small things I've noticed:

  1. if I change the params and the fields_for to user_credential (removing the plural) the email field disappears.
  2. If I keep the params as is I get this exception: ActiveModel::UnknownAttributeError (unknown attribute 'user_credentials' for UserAccount.):

I've seen people recommending adding a @user.user_credential.build() to the new method but doing that just gives me a nil:NilClass error.

Upvotes: 0

Views: 286

Answers (1)

Imam
Imam

Reputation: 1316

I guess you need to change user_credentials to user_credentials_attributes

 def user_params
    params.require(:user_account).permit(user_credentials_attributes: [:email])
 end

When nested attributes are submitted through a form, the _attributes tag is appended for the nested attributes parent.

Upvotes: 1

Related Questions