Craig
Craig

Reputation: 159

Rails - Autocreate an associated model

I'm new to rails and I'm creating a simple app that has clients with addersses. After getting some advice and views from the stack overflow community, I decided to save addresses as a seperate model

I am now trying to implement this in my app but I'm having problems getting the address to save correctly from the "new client" form. here is my code so far:

class Address < ActiveRecord::Base
    belongs_to :client
end

class Client < ActiveRecord::Base
    has_one :address
    before_create :build_address, unless: Proc.new { |client| client.address }
end



<%= form_for(@client) do |f| %>
  <% if @client.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@client.errors.count, "error") %> prohibited this client from being saved:</h2>

      <ul>
      <% @client.errors.full_messages.each do |message| %>
        <li><%= message %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.label :phone_number %><br>
    <%= f.text_field :phone_number %>
  </div>

  <%= f.fields_for :address do |a| %>
    <div class="field">
      <%= a.label :house_number %><br>
      <%= a.number_field :house_number %>
    </div>
    <div class="field">
      <%= a.label :house_name %><br>
      <%= a.text_field :house_name %>
    </div>
    <div class="field">
      <%= a.label :post_code %><br>
      <%= a.text_field :post_code %>
    </div>
  <% end %>

  <div class="actions">
    <%= f.submit %>
  </div>

<% end %>

With this, the client is created successfully but the address record is created with empty fields. No errors.

Any help would be most appreciated.

Thanks

Upvotes: 1

Views: 243

Answers (2)

max
max

Reputation: 102249

First off - there are very few situations where ActiveModel callbacks don't cause grief. Usually putting logic into your models is a good thing - but getting callbacks to run just when you need them and not for example in unrelated tests is near impossible.

In this case you only need to build the address so that the form inputs are pre populated in your new action. There is no other reason for all your Client instances to have an empty address record piggybacking on them all the time.

So instead we would do it like this:

class Client < ActiveRecord::Base
  has_one :address
  accepts_nested_attributes_for :address
end

class ClientController < ApplicationController
  def new 
    @client = Client.new
    @client.build_address
  end

  def create
    @client = Client.create(client_params)
    # ...
  end

  def client_params
    params.require(:client)
          .permit(
             :name, :phone_number, 
             address_attributes: [:house_number, :house_name]
          )
  end
end

Upvotes: 3

Richard Peck
Richard Peck

Reputation: 76784

You'll need accepts_nested_attributes_for:

#app/models/client.rb
class Client < ActiveRecord::Base
    has_one :address
    accepts_nested_attributes_for :address
    before_create :build_address, unless: Proc.new { |client| client.address }
end

This will allow you to do the following:

#app/controllers/clients_controller.rb
class ClientsController < ApplicationController
   def new
      @client = Client.new
      @client.build_address
   end
end

This should work for you.

Upvotes: 2

Related Questions