2016-03-02 23 views
7

私はまだの作成/更新に対処する方法を試しています。Ecto。私はthe docsと同様に、José'sの投稿を再読しましたが、まだ苦労しています。has_many、Ectoでの関連付け

ウェブ/モデル/ dish.ex

defmodule Mp.Dish do 
    use Mp.Web, :model 

    schema "dishes" do 
    # ... 
    has_many :dish_dietary_prefs, Mp.DishDietaryPref, on_delete: :delete_all, 
     on_replace: :delete 
    has_many :dietary_prefs, through: [:dish_dietary_prefs, :dietary_pref] 
    end 

    # ... 
end 

ウェブ/モデル/

defmodule Mp.DietaryPref do 
    use Mp.Web, :model 

    schema "dietary_prefs" do 
    # ... 
    has_many :dish_dietary_prefs, Mp.DishDietaryPref, on_delete: :delete_all, 
     on_replace: :delete 
    has_many :dishes, through: [:dish_dietary_prefs, :dish] 
    end 

    # ... 
end 

ウェブ/モデルをdietary_pref.ex:私はこれで持っているもの

/dish_dietary_pref.ex

defmodule Mp.DishDietaryPref do 
    use Ecto.Schema 

    schema "dish_dietary_prefs" do 
    belongs_to :dish, Mp.Dish 
    belongs_to :dietary_pref, Mp.DietaryPref 
    end 
end 

Iは、Dishのパラメータを受信するJSONエンドポイントを持っている私がカンマで区切られた文字列として渡されdietary_prefsと呼ばれるキーを有する内部ので、例えば:

[info] POST /api/vendors/4/dishes 
[debug] Processing by Mp.Api.DishController.create/2 
    Parameters: %{"dish" => %{"dietary_prefs" => "2,1"}, "vendor_id" => "4"} 

(付このために削除"dish"のための追加のパラメータは、SO投稿してください。)

私は私のコントローラでこれを処理するにはどうすればよい

  1. POSTの要求(アクションを作成)、与えられたDietaryPref sのこの新しいDishを関連付けるdish_dietary_prefsに必要なレコードを作成します。具体的には、私はこの動作をします。カンマで区切られた文字列は、DietaryPrefレコードの場合はidです。
  2. dish_dietary_prefsに必要なレコードを作成/破棄して関連付けを更新します(ユーザーは異なる食事設定に料理を再割り当てできます)。
  3. DELETEリクエストの場合は、dish_dietary_prefsを破棄してください。このケースは既にモデルのon_delete設定で処理されていると思います。

は、私はすでに(単純has_many/belongs_to関係で)与えられたベンダーのための/更新の料理を作成するために私のコントローラのロジックを持っているが、私はまだこれらの関連性を破壊する/作成/更新する方法を見つけ出すことはできません与えられた料理のために。

ご協力いただければ幸いです。


私は"need to receive the IDs and manually build the intermediate association for each"DietaryPref私はDishに関連付けていますならば、私は私のコントローラで上記の仕様にそれを行うだろうかの例を得ることができますか?


UPDATE:ちょうどエクト2.0.0-beta.1が出ていることを見て、それはそれは私の問題への解決策になるように見えた、supports many_to_manyです。誰も私が上記のように、それを実際に使用している例がありますか?独特のジェダイ・マスター・ホセ・Valim自身へ

答えて

8

おかげで、私はこれが(エクト2.0.0-beta.1に)考え出し持っている:

def create(conn, %{"dish" => dish_params }, vendor) do 
    dietary_prefs = get_dietary_pref_changeset(dish_params["dietary_prefs"]) 

    changeset = vendor 
    |> build_assoc(:dishes) 
    |> Repo.preload(:dietary_prefs) 
    |> Dish.changeset(dish_params) 
    |> Ecto.Changeset.put_assoc(:dietary_prefs, dietary_prefs) 

    case Repo.insert(changeset) do 
    {:ok, dish} -> 
     conn 
     |> put_status(:created) 
     |> render("show.json", dish: dish) 
    {:error, changeset} -> 
     conn 
     |> put_status(:unprocessable_entity) 
     |> render(ChangesetView, "error.json", changeset: changeset) 
    end 
end 

def update(conn, %{"id" => id, "dish" => dish_params}, vendor) do 
    dish = Repo.get!(vendor_dishes(vendor), id) 
    dietary_prefs = get_dietary_pref_changeset(dish_params["dietary_prefs"]) 

    changeset = dish 
    |> Repo.preload(:dietary_prefs) 
    |> Dish.changeset(dish_params) 
    |> Ecto.Changeset.put_assoc(:dietary_prefs, dietary_prefs) 

    case Repo.update(changeset) do 
    { :ok, dish } -> 
     render(conn, "show.json", dish: dish) 
    { :error, changeset } -> 
     conn 
     |> put_status(:unprocessable_entity) 
     |> render(ChangesetView, "error.json", changeset: changeset) 
    end 
end 

defp vendor_dishes(vendor) do 
    assoc(vendor, :dishes) 
end 

defp parse_dietary_pref_ids(ids) do 
    ids 
    |> String.split(",") 
    |> Enum.map(fn(x) -> Integer.parse(x) |> Kernel.elem(0) end) 
end 

defp get_dietary_prefs_with_ids(ids) do 
    from(dp in DietaryPref, where: dp.id in ^ids) |> Repo.all 
end 

defp get_dietary_pref_changeset(param) do 
    param 
    |> parse_dietary_pref_ids 
    |> get_dietary_prefs_with_ids 
    |> Enum.map(&Ecto.Changeset.change/1) 
end 

https://groups.google.com/forum/#!topic/elixir-ecto/3cAi6nrsawk

+0

は、ここに私の最後のコントローラです'put_assoc/3'が正しく機能するためには、' has_many X、through:Y'の代わりに 'many_to_many'を使う必要があることに注意してください。これを示すリストについては、Googleグループのリンクをご覧ください。 – Schrockwell

関連する問題