Neil Neil - 5 months ago 9
Ruby Question

accepts_nested_attributes_for with validations and using find_or_create_by

I have a

user
model and a
town
model. A
user
belongs_to a
town
:

# models/user.rb
class User < ApplicationRecord
belongs_to :town
accepts_nested_attributes_for :town
validates :last_name, presence: true
end





# models/town.rb
class Town < ApplicationRecord
has_many :users
validates :name, presence: true
validates :name, uniqueness: true
end


You go to create a new
user
record: there is a text_box in order to put in the associated town's
name
. When submitted, the rails application should do the following:


  • use
    find_or_create_by
    . go and check if there already exists a
    town
    record with the
    name
    attribute passed in.


    • If a
      town
      record DOES exist with that given
      name
      attribute, just associate that existing
      town
      record to this new
      user
      record

    • If a
      town
      record DOES NOT exist with that given
      name
      attribute, then create a new
      town
      record with that
      name
      attribute, and associate that one to this
      user
      record


  • If any validations fail, do not create a
    user
    record or a
    town
    record and re-render the form.



I am having real trouble with this. This question suggests putting
autosave
on the
belongs_to :town
statement, as well as defining a method called
autosave_associated_records_for_town
. However: I just could not get that to work.

Appreciate it!

Answer

Please, try that solution. It works for me.

User

# user.rb

class User < ActiveRecord::Base
  belongs_to :town

  accepts_nested_attributes_for :town
  validates :last_name, presence: true
  validates :town, presence: true
end

Town

# town.rb

class Town < ActiveRecord::Base
  has_many :users
  validates :name, presence: true
end

Controller

# users_controller.rb

respond_to :html    

def create

  # ...
  @user = User.new(user_params)
  @user.town = Town.find_or_create_by(user_params[:town_attributes])
  if @user.save
    respond_with(@user)
  else
    render 'new'
  end
end

# ...

def user_params
  params.require(:user).permit(:last_name, :email, :town_id, town_attributes: [:name])
end

View

# users/_form.html.erb

<%= form_for(@user) do |f| %>

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

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

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


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

  <%= f.fields_for :town, @town do |bldr| %>
    <div class="field">
      <%= bldr.label :name, 'Town name' %><br>
      <%= bldr.text_field :name %>
    </div>
  <% end %>

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

Validations