Ecto.multi 的动态插入数
Dynamic number of inserts with Ecto.multi
我的应用程序中有一个 Foo
模式 has_many
模式 Bar
.
schema "foos" do
field :content, :string
has_many :bars, MyApp.Content.Bar, foreign_key: :foo_id
end
schema "bars" do
field :content, :string
belongs_to :foo, MyApp.Content.Foo, foreign_key: :foo_id
end
我想要一个为 Foo
取一个 id 的函数,创建该 Foo
的副本并插入它,然后创建所有关联的 Bar
的副本新 Foo
。给定一个具有许多子 Bar 的 Foo,此函数将一次性复制该 Foo 和所有这些 Bar。
我使用 Ecto.Multi 来处理这样的事情,但我不确定如何为可变数量的操作设置它。到目前为止我有这个:
resolve fn (%{foo_id: foo_id}, _info) ->
oldfoo = Repo.get!(Foo, foo_id)
multi =
Multi.new
|> Multi.run(:foo, fn %{} ->
MyApp.Content.create_foo(%{
content: oldfoo.content
}) end)
|> Multi.run(:bars, fn %{foo: foo} ->
query =
from b in Bar,
where: b.foo_id == ^foo.id,
select: b.id
bars = Repo.all(query) # returns a list of id/ints like [2,3,6,7,11...]
Enum.map(bars, fn barid ->
bar = Repo.get(Bar, barid)
Bar.changeset(%Bar{}, %{
content: bar.content,
foo_id: foo.id
})
|> Repo.insert()
end)
end)
case Repo.transaction(multi) do
{:ok, %{foo: foo}} ->
{:ok, foo}
{:error, _} ->
{:error, "Error"}
end
这会引发错误:
** (exit) an exception was raised:
** (CaseClauseError) no case clause matching: [ok: %MyApp.Content.Foo...
在 Ecto.Multi 内是否有合理的方法来做到这一点?
我会在这里使用 Enum.reduce_while/3
。我们遍历列表,收集所有插入的柱。如果任何插入失败,我们 return 那个错误,否则我们 return 列表中收集的值。
Enum.reduce_while(bars, {:ok, []}, fn barid, {:ok, acc} ->
bar = Repo.get(Bar, barid)
Bar.changeset(%Bar{}, %{
content: bar.content,
foo_id: foo.id
})
|> Repo.insert()
|> case do
{:ok, bar} -> {:cont, {:ok, [bar | acc]}}
{:error, error} -> {:halt, {:error, error}}
end
end)
我的应用程序中有一个 Foo
模式 has_many
模式 Bar
.
schema "foos" do
field :content, :string
has_many :bars, MyApp.Content.Bar, foreign_key: :foo_id
end
schema "bars" do
field :content, :string
belongs_to :foo, MyApp.Content.Foo, foreign_key: :foo_id
end
我想要一个为 Foo
取一个 id 的函数,创建该 Foo
的副本并插入它,然后创建所有关联的 Bar
的副本新 Foo
。给定一个具有许多子 Bar 的 Foo,此函数将一次性复制该 Foo 和所有这些 Bar。
我使用 Ecto.Multi 来处理这样的事情,但我不确定如何为可变数量的操作设置它。到目前为止我有这个:
resolve fn (%{foo_id: foo_id}, _info) ->
oldfoo = Repo.get!(Foo, foo_id)
multi =
Multi.new
|> Multi.run(:foo, fn %{} ->
MyApp.Content.create_foo(%{
content: oldfoo.content
}) end)
|> Multi.run(:bars, fn %{foo: foo} ->
query =
from b in Bar,
where: b.foo_id == ^foo.id,
select: b.id
bars = Repo.all(query) # returns a list of id/ints like [2,3,6,7,11...]
Enum.map(bars, fn barid ->
bar = Repo.get(Bar, barid)
Bar.changeset(%Bar{}, %{
content: bar.content,
foo_id: foo.id
})
|> Repo.insert()
end)
end)
case Repo.transaction(multi) do
{:ok, %{foo: foo}} ->
{:ok, foo}
{:error, _} ->
{:error, "Error"}
end
这会引发错误:
** (exit) an exception was raised:
** (CaseClauseError) no case clause matching: [ok: %MyApp.Content.Foo...
在 Ecto.Multi 内是否有合理的方法来做到这一点?
我会在这里使用 Enum.reduce_while/3
。我们遍历列表,收集所有插入的柱。如果任何插入失败,我们 return 那个错误,否则我们 return 列表中收集的值。
Enum.reduce_while(bars, {:ok, []}, fn barid, {:ok, acc} ->
bar = Repo.get(Bar, barid)
Bar.changeset(%Bar{}, %{
content: bar.content,
foo_id: foo.id
})
|> Repo.insert()
|> case do
{:ok, bar} -> {:cont, {:ok, [bar | acc]}}
{:error, error} -> {:halt, {:error, error}}
end
end)