2016-03-02 10 views
7

Sto ancora cercando di capire come gestire la creazione/aggiornamento delle associazioni has_many, through: in Ecto. Ho riletto il post José's sulle associazioni e su the docs, ma sto ancora lottando.has_many, tramite associazioni in Ecto

Quello che ho è questa:

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

Ho un endpoint JSON che riceve i parametri per un Dish, all'interno del quale ho una chiave definito dietary_prefs che viene passato come stringa delimitata da virgole, così, ad esempio:

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

(Con parametri aggiuntivi per "dish" rimossi per questo SO posta.)


Come faccio a gestire questo nel mio controllore? In particolare, voglio questo comportamento:

  1. Per POST richieste (creare azioni), creare i record necessari dish_dietary_prefs associare questo nuovo Dish con il dato DietaryPref s. La stringa delimitata da virgola è id s per i record DietaryPref.
  2. Per le richieste PUT/PATCH (aggiornamenti), creare/distruggere i record necessari in dish_dietary_prefs per aggiornare le associazioni (gli utenti possono riassegnare i piatti alle diverse preferenze alimentari).
  3. Per le richieste DELETE, distruggere dish_dietary_prefs. Penso che questo caso sia già stato gestito con la configurazione on_delete nei modelli.

ho già la logica nel mio controller per creare/stoviglie di aggiornamento per un determinato fornitore (che è solo un semplice rapporto has_many/belongs_to), ma non riesco ancora a capire come creare/aggiornare/distruggere queste associazioni per un dato piatto.

Qualsiasi aiuto sarebbe molto apprezzato.


Se io "need to receive the IDs and manually build the intermediate association for each"DietaryPref sto Associazione alla Dish, ho potuto ottenere un esempio di come avrei fatto che alla specifica di cui sopra in mio controller?


UPDATE: solo vedere che Ecto-2.0.0 beta.1 è fuori, e questo è supports many_to_many, che sembra che sarebbe una soluzione al mio problema. Qualcuno ha un esempio di utilizzo in azione, come ho descritto sopra?

risposta

8

Grazie alla inimitabile Jedi-Master José Valim se stesso, ho ottenuto questo capito (in Ecto 2.0.0-beta.1):

Ecco il mio controllo finale:

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

Nota che devi anche usare 'many_to_many' (invece di' has_many X, through: Y') per fare in modo che 'put_assoc/3' funzioni correttamente. Vedi il link dei gruppi di Google per la lista che mostra questo. – Schrockwell

Problemi correlati