2016-03-02 10 views
7

Ich versuche immer noch zu erraten, wie man mit dem Erstellen/Aktualisieren has_many, through: Assoziationen in Ecto umzugehen. Ich habe José's Post auf Assoziationen sowie the docs wieder gelesen, aber ich kämpfe immer noch.has_many, durch Assoziationen in Ecto

Was ich habe, ist dies:

web/models/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 

web/models/dietary_pref.ex

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 

web/Modelle /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 

Ich habe einen JSON Endpunkt, der Parameter für eine Dish empfängt, in dessen Inneren I einen Schlüssel dietary_prefs genannt, die als kommagetrennte Zeichenkette übergeben wird, so zum Beispiel:

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

(mit zusätzliche Parameter für "dish" für diese SO Postens enthoben.)


Wie gehe ich in meinem Controller? Insbesondere möchte ich dieses Verhalten:

  1. Für POST Anfragen (Erstellen Aktionen), erstellen die notwendigen Datensätze in dish_dietary_prefs diese neue Dish mit den gegebenen DietaryPref s zuzuordnen. Die durch Kommas getrennte Zeichenfolge ist id s für DietaryPref Datensätze.
  2. Für PUT/PATCH Anfragen (Updates), erstellen/zerstören Sie die notwendigen Datensätze in dish_dietary_prefs, um die Assoziationen zu aktualisieren (Benutzer können Gerichte zu verschiedenen Diät-Einstellungen neu zuweisen).
  3. Für DELETE Anfragen, zerstören dish_dietary_prefs. Ich denke, dieser Fall wird bereits mit der on_delete Konfiguration in den Modellen behandelt.

Ich habe bereits die Logik in meinem Controller/Update Gerichte für einen bestimmten Anbieter zu schaffen (die nur eine einfache has_many/belongs_to Beziehung ist), aber ich kann immer noch nicht herausfinden, wie/Update erstellen/zerstören diese Assoziationen für ein bestimmtes Gericht.

Jede Hilfe würde sehr geschätzt werden.


Wenn ich "need to receive the IDs and manually build the intermediate association for each"DietaryPref ich zum Dish bin zuordnet, könnte ich ein Beispiel dafür, wie ich, dass auf die obige Beschreibung in meinem Controller tun würde?


UPDATE: Just zu sehen, dass Ecto 2.0.0-beta.1 out ist, und das ist supports many_to_many, die, wie es eine Lösung für mein Problem wäre aussieht. Hat jemand ein Beispiel dafür, es in Aktion zu benutzen, wie ich es oben beschrieben habe?

Antwort

8

Dank des unnachahmlichen Jedi-Meister José Valim selbst, ich habe dies herausgefunden habe (in Ecto 2.0.0-beta.1):

mein letzten Controller hier:

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

Beachten Sie, dass Sie auch 'many_to_many' (anstelle von' has_many X, through: Y') verwenden müssen, um das 'put_assoc/3' richtig zu funktionieren. Sehen Sie sich den Google Groups-Link für den Eintrag an, der dies anzeigt. – Schrockwell

Verwandte Themen