Reputation: 1953
I have three models, companies
, transactions
and subtransactions
. Each has_many of the next. I need to build a form that saves to both transactions
and multiple records of subtransactions
.
I'm struggling with the best approach to the form and the appropriate way to save to the DB. Ideally, the records get saved at once if all pass validation.
My simplified form:
<%= form_for(@transaction) do |f| %>
<div class="field" id="transaction_amount">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<% 1.upto(5) do |i| %>
<%= fields_for("subtransaction[#{i}]") do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal, "Subtotal #{i}" %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
<% end %>
My TransactionsController:
def new
@transaction = company.transactions.build if logged_in?
end
def create
@transaction = company.transactions.build(transaction_params)
params[:subtransaction].each do |i|
# build and save
end
if @transaction.save ## @subtransaction.save
flash[:success] = "Success!"
redirect_to success_url
else
render :new
end
end
Upvotes: 2
Views: 5360
Reputation: 76774
The way to do this is to pass the nested
attributes through the various models:
#app/models/company.rb
class Company < ActiveRecord::Base
has_many :transactions
accepts_nested_attributes_for :transactions
end
#app/models/transaction.rb
class Transaction < ActiveRecord::Base
has_many :sub_transactions
accepts_nested_attributes_for :sub_transactions
end
This will give you the ability to use the following:
#app/controllers/transactions_controller.rb
class TransactionsController < ApplicationController
def new
@transaction = company.transactions.new
5.times do
@transaction.sub_transactions.build
end
end
def create
@transaction = company.transactions.new transaction_params
@transaction.save
end
private
def transaction_params
params.require(:transaction).permit(sub_transactions_attributes: [:subtotal])
end
end
Then...
#app/views/transactions/new.html.erb
<%= form_for @transaction do |f| %>
<%= f.fields_for :sub_transactions do |s| %>
<%= s.text_field :subtotal %>
<% end %>
<%= f.submit %>
<% end %>
This will allow you to save the various nested attributes through the main object. It is the correct, conventional, way to do it.
Upvotes: 5
Reputation: 1716
class Transaction < ActiveRecord::Base
has_many :subtransations
accepts_nested_attributes_for :subtransactions
end
def new
@transaction = Transaction.new
@transaction.subtransactions.build
end
def create
company = Company.first # Or whatever you need.
# We merge the company id to the transaction params since
# a transaction belongs_to a company.
@transaction = Transaction.create!(create_params.merge(company_id: company.id))
end
private
def create_params
params.require(:transaction).permit(
:total,
subtransactions_attributes: [:id, :subtotal]
)
end
<%= form_for(@transaction) do |f| %>
<div class="field">
<%= f.label :total %><br>
<%= f.text_field :total %>
</div>
<%= f.fields_for :subtransaction do |s| %>
<div class="field" id="subtotal_amount">
<%= s.label :subtotal %><br>
<%= s.text_field :subtotal %>
</div>
<% end %>
<% end %>
This will allow to create only one subtransaction with the transaction record
If you want to create more than one subtransaction
record you should use cocoon gem, it's easy to setup and will save much precious time.
Upvotes: 2