How to create multiple records at once?

Let’s say we have 3 entities

schema "customers" do
  has_many :addresses, Address, foreign_key: :customer_id
end

schema "addresses" do
  field :customer_id #illustrative purppose
  belongs_to :customer, Customer, foreign_key: :customer_id
end

schema "orders" do
  field :customer_id #illustrative purppose

  belongs_to :customer, Customer, foreign_key: :customer_id
end

An addresscontains customer_id and an order contains customer_id

How do i create all 3 entities at the same time?

I have tried

customer = Customer.changeset(%Customer{}, %{})
address = Address.changeset(%Address{}, %{}) |> Ecto.Changeset.put_assoc(:customer, customer)
order = Order.changeset(%Order{}, %{}) |> Ecto.Changeset.put_assoc(:customer, customer)

However, this would create 2 customers, 1 address and 1 order instead of 1 customer, 1 address and 1 order.

customer = Customer.changeset(%Customer{}, %{})
address = build_assoc(customer, :addresses) |> Address.changeset(%{})
order = build_assoc(customer, :orders) |> Order.changeset(%{})

And if you need to rollback if any of them fail wrap those lines in a Ecto.Multi.

1 Like

Thanks LostKobrakai for your reply. I am having a problem trying your solution

To make build_assoc work, I found Customerhas to be fetched from DB first.
If using the following directly,

customer = Customer.changeset(%Customer{}, %{})
address = build_assoc(customer, :addresses) |> Address.changeset(%{})

it is going to throw
** (UndefinedFunctionError) function Ecto.Changeset.__schema__/2 is undefined or private

Instead, the following will not throw error.

customer = Repo.get(Customer, 1)
address = build_assoc(customer, :addresses) |> Address.changeset(%{})

@chungwong It would be nice to see the full Customer and Address files to be able to debug more :slight_smile:

Ah sure, missed that because in Ecto.Multi you’d insert the customer into the db before creating the associations.

A note about your own put_assoc version from the docs:

When updating the data, this function requires the association to have been preloaded in the changeset struct.