2016-05-24 16 views
1

私はEcto 2 rcを試しています。モデルをectoの既存モデルと多対多の関係に挿入する2

私のモデルは以下のとおりです。

schema "containers" do 
    field :name, :string 
    many_to_many :items, Test.Item, join_through: Test.ContainerItem, on_delete: :delete_all 

    timestamps 
end 

schema "items" do 
    field :content, :string 
    many_to_many :containers, Test.Container, join_through: Test.ContainerItem, on_delete: :delete_all 

    timestamps 
end 

schema "containers_items" do 
    belongs_to :container, Test.Container 
    belongs_to :item, Test.Item 

    timestamps 
end 

そして、私のコントローラのコードは次のとおりです。

def add_item(conn, %{"item" => item_params, "container_id" => container_id}) do 
    item = Item.changeset(%Item{}, item_params) 
    IO.inspect(item) #TODO remove 
    container = Container |> Repo.get(container_id) |> Repo.preload([:items]) 
    IO.inspect(container) #TODO remove 

    changeset = container 
    |> Ecto.Changeset.change 
    |> Ecto.Changeset.put_assoc(:items, [item]) 

    IO.inspect(changeset) #TODO remove 

    if changeset.valid? do 
    Repo.update(changeset) 

    conn 
    |> put_flash(:info, "Item added.") 
    |> redirect(to: container_path(conn, :show, container)) 
    else 
    render(conn, "show.html", container: container, changeset: changeset) 
    end 
end 

私はコンテナに単一の項目を追加している場合さて、これは正常に動作します。アイテムがコンテナに存在する場合しかし、その後、別の項目を追加しようとすると、私を与える:

(例外RuntimeError)あなたが関係変更しようとしている。 Test.Containerの項目を、しかし、欠けているデータがあります。

私はこれを間違った方法でやっていると感じることはできませんが、いくつかのアドバイスをいただければ幸いです。

答えて

3

これでわかりました。

私の問題はChangesetsにアイテムを変換していないため、ectoが変更を追跡できるようになりました。

私が作成する必要があった唯一の編集はコントローラです。

これは、代わりに次のようになります。これは、私は、これはこれを行うには正しい方法であることに困惑しています私の問題で私を助けている間

def add_item(conn, %{"item" => item_params, "container_id" => container_id}) do 
    item = Item.changeset(%Item{}, item_params) 
    IO.inspect(item) #TODO remove 
    container = Container |> Repo.get(container_id) |> Repo.preload([:items]) 
    IO.inspect(container) #TODO remove 

    item_changesets = Enum.map([item | container.items], &Ecto.Changeset.change/1) 

    changeset = container 
    |> Ecto.Changeset.change 
    |> Ecto.Changeset.put_assoc(:items, item_changesets) 

    IO.inspect(changeset) #TODO remove 

    if changeset.valid? do 
    Repo.update(changeset) 

    conn 
    |> put_flash(:info, "Item added.") 
    |> redirect(to: container_path(conn, :show, container)) 
    else 
    render(conn, "show.html", container: container, changeset: changeset) 
    end 
end 
+0

。アソシエーションを追加するには、既存のすべてのアソシエーションをロードし、変更セットとしてリキャストしてから、1つのアイテムを追加する必要があります。これは非常に非効率的です。 – Owen

関連する問題