How to use nested forms in Rails if the fields have the same name?

661 Views Asked by At

I have two models, Dog and Owner, and I want their names to be the same, and would be redundant if I asked to fill out the fields twice (once for the dog and another time for the owner). I'm wondering if there's a simpler way to update the two databases with one input.

<h1>Create a new Dog:</h1>
<%= form_for(@dog) do |f|%>
  <div>
    <%= f.label :name%>
    <%= f.text_field :name%>
  </div><br>
    
  <div>
    <%= f.label :breed%>
    <%= f.text_field :breed%>
  </div><br>
    
  <div>
    <%= f.label :age%>
    <%= f.text_field :age%>
  </div><br>
    
  <div>
    <h3>create a new owner:</h3>
    <%= f.fields_for :owner, Owner.new do |owner_attributes|%>
        <%= owner_attributes.label :name, "Owner Name:" %>
        <%= owner_attributes.text_field :name %>
    <% end %>
  </div>
    
  <%= f.submit %>
    
<% end %>
2

There are 2 best solutions below

2
On BEST ANSWER

First of all, not sure why you want to keep the name of the owner and the dog same.

However, there can be many ways to achieve what you want:

  1. You can simply omit the owner name from the form.

So you no longer need: <%= owner_attributes.label :name, "Owner Name:" %> OR you no longer need:

<div>
    <%= f.label :name%>
    <%= f.text_field :name%>
  </div><br>

And in the Owner/Dog model, you can pass the name of the dog/owner in a callback - maybe after_initialize or before_save or before_validation depending on your validation requirements.

class Dog
  belongs_to :owner
  before_validation :set_name

  private

  def set_name
    self.name = owner&.name
  end
end
  1. You can make the owner name as a hidden field instead and can write some javascript to update the hidden field with the dog name before submitting the form or onblur event. I would prefer the first approach since it's simpler and more secure than only JS solution to maintain database consistency
0
On

If dogs belongs_to and owner, you don't really need to store the owner's name separately. You can just call dog.owner.name anywhere you have a Dog instance. Having said that, it is relatively straightforward to append attributes on top of the POSTed form values in your controller using .merge():

def create
  @dog = Dog.new(dog_params.merge(owner: params[:dog][:owner])[:name])
  if @dog.save
    ...
  end
end

def dog_params
  params.require(:dog).permit(:name, :breed, :age, owner: %i[name])
end