Ecto multi build_assoc 在列表中

Ecto multi build_assoc on a list

我想在 has_many 的多事务中插入一个列表。像这样的东西:

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()

正如我所评论的那样,我没有找到可行的解决方案。

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

这只插入一行,我有一个列表要插入。

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

这不是 return multi,也不会与 main 建立关联。

我也找到了这个表格,但它也适用于单个插入:

|> 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)

Ecto.Multi不是在插入多条记录之后命名的,而是在单个事务中分组多个操作之后命名的。

Ecto.Multi.insert_all/5, but it has the same limitations as Repo.insert_all/3,即无法插入到多个表中。 (此外,它不支持自动时间戳,还有其他一些限制。)

只需使用正常的迭代 Enum.each/2 或类似的方法,根据需要生成尽可能多的插入语句,并将它们全部包装到一个事务中。


要将多个 children 收集到 Ecto.Multi 中,可以使用 Enum.reduceEcto.Multi 作为累加器:

multi =
  Ecto.Multi.new()
  |> Ecto.Multi.insert(:main, Main.changeset(%Main{}, attrs))

attrs.children
|> Enum.reduce(
    multi,
    &Ecto.Multi.insert(&2, :child, create_child(&1))
  )
|> Repo.transaction()