Ecto multi build_assoc on a list

Hello,

I want to insert a list in a multi transaction with a has_many. Something like :

Ecto.Multi.new()
|> Ecto.Multi.insert(:main, Main.changeset(%Main{}, attrs))
|> Ecto.Multi.insert(:child, fn %{main: main} ->
  Ecto.build_assoc(main, :child, %{name: "aaa"}) # inserts just one
end)
|> Ecto.Multi.run(:child, fn _, %{main: main} ->
  Enum.map(attrs.children, &create_child/1) # doesn't return a multi and doesn't build the assoc
end)
|> Repo.transaction()

As i commented out, I don’t find a working solution.

|> Ecto.Multi.insert(:child, fn %{main: main} ->
  Ecto.build_assoc(main, :child, %{name: "aaa"})
end)

This inserts just one row, I have a list to insert.

|> Ecto.Multi.run(:child, fn _, %{main: main} ->
  Enum.map(attrs.children, &create_child/1)
end)

This doesn’t return a multi and doesn’t build the assoc with main.

I found this form also, but it’s intended for a single insert as well :

|> Multi.run(:user, fn %{organisation: organisation} ->
  User.changeset(User, user_params)
  |> Ecto.Changeset.put_assoc(organisation)
  |> Repo.insert # returns a {:ok, user} or {:error, changeset}
end)

Thanks ahead

:wave:

What’s in create_child/1?

Hi :slight_smile:

It’s boilerplate :

%Child{}
|> Child.changeset(attrs)
|> Repo.insert()

Found a solution - running a Enum.reduce into a Multi.run

  |> Multi.run(:children, fn _, %{main: main} ->
    case Map.has_key?(attrs, :children) do
      true ->
        Enum.reduce(attrs.children, [], fn child, _ ->
          create_child(Enum.into(%{main_id: main.id}, child))
        end)

      false ->
        {:ok, :noop}
    end
  end)
2 Likes