2017-12-30 26 views
0

Foo 스키마는 has_many 스키마 Bar입니다.Dynamic inserts with Ecto.multi

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의 복사본을 생성하고 삽입 한 다음 새 Foo과 관련된 모든 Bar의 복사본을 생성합니다. 많은 하위 막대가있는 Foo를 제공하면이 함수는 Foo와 모든 막대를 한 번에 복사합니다.

저는 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 내에서이 작업을 수행 할 수있는 합리적인 방법이 있나요를?

답변

1

여기에 Enum.reduce_while/3을 사용하고 싶습니다. 삽입 된 모든 모음을 수집하여 목록을 반복합니다. 삽입이 실패하면 오류를 반환하고, 그렇지 않으면 수집 된 값을 목록에 반환합니다.

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)