Jon Gjengset Jon Gjengset - 17 days ago 9
Ruby Question

Creating multiple unrelated associated model instances in Rails

I have what I believe is fairly simple model setup:

class Booking < ApplicationRecord
has_many :payments
end
class Payment < ApplicationRecord
belongs_to :booking
end


Now, I want to create a form that allows a user to register payments in batch. That is, the form should have a number of input rows, each one representing a payment for some booking (i.e., each row has some fields for the columns of
Payment
plus a
booking_id
field). Upon submitting, each row should cause the creation of a corresponding
Payment
, which should be associated with the
Booking
indicated by the user for that row.

This seems to be surprisingly tricky, and my Google-Fu is failing me. I've tried the following (inspired by this post describing a solution without associations), which I thought would work, but which, well, doesn't:

class Admin::PaymentController < Admin::Controller
def batch
@payments = []
5.times do
@payments << Payment.new
end
end

def submit
params["payments"].each do |payment|
if payment["booking_id"] != "" || payment["amount"] != ""
Payment.create(payment_params(payment))
end
end
end

private
def payment_params(p)
p.permit(:booking_id, :amount)
end
end


<%= form_tag admin_payment_submit_path do %>
<% @payments.each do |payment| %>
<%= fields_for 'payments[]', payment do |p| %>
<%=p.text_field :booking_id%>
<%=p.number_field :amount%>
<% end %>
<% end %>
<%= submit_tag %>
<% end %>


This renders the form without erroring out, but the HTML names work out such that only a single payment (the last one) is submitted (e.g.,
name="payments[booking_id]"
). Furthermore, upon submitting, I get the error

undefined method `permit' for "booking_id":String Did you mean? print


Which is less than helpful.

I've tried other variations too, but I feel like at this point I'm just feeling my way in the dark. Any guidance would be greatly appreciated!

Answer

params in controller is a instance of ActiveController::Parameter that has permit method.
But params["payments"] is a just array as subset of params.

For multiple payment params

def submit
  payment_params.each do |payment|
    if payment["booking_id"].present? || payment["amount"].present?
      Payment.create(payment)
    end
  end
end

private

def payment_params
  params.permit(payments: [:booking_id, :amount])["payments"]
end

For Single payment param

def submit
  if payment_param["booking_id"].present? || payment_param["amount"].present?
    Payment.create(payment_param)
  end
end

private
def payment_param
  params.require(:payments).permit(:a, :b)
end