5

Come si aggiorna un modello con un'associazione nidificata (utilizzando [Elixir, Phoenix, Ecto])?Ecto - Aggiornamento delle associazioni nidificate (polimorfo)

Ho provato quanto segue, per considerarlo come parte dell'aggiornamento del genitore, senza successo (utilizzando platformatec blog come ispirazione).

Modelli:

schema "user" do 
    has_one :address, {"users_addresses", MyApp.Address}, foreign_key: :assoc_id 
    end 
    @required_fields ~w(address) 

------ 

    # Materialized in users_addresses table 
    schema "abstract table: addresses" do 
    field :assoc_id,  :integer 
    field :street_address, :string 
    end 

Request (PATCH):

{ 
    "user" => { 
    "address" => { 
     "street_address" => "1234" 
    } 
    } 
} 

Controller:

def update(conn, %{"id" => id, "user" => params}) do 
    user = MyApp.Repo.get(User, id) 
    |> MyApp.Repo.preload [:address] 

    if is_nil(user) do 
    send_resp(conn, 404, "") 
    else 
    changeset = User.changeset(user, params) 

    if changeset.valid? do 
     case MyApp.Repo.update(changeset) do 
     {:ok, model} -> send_resp(conn, 204, "") 
     {:error, changeset} -> conn 
      |> put_status(:unprocessable_entity) 
      |> render(MyApp.ChangesetView, "error.json", changeset: changeset) 
     end 
    else 
     conn 
     |> put_status(:unprocessable_entity) 
     |> render(MyApp.ChangesetView, "error.json", changeset: changeset) 
    end 
    end 
end 

Changeset (da tronchi):

%Ecto.Changeset{action: nil, 
    changes: %{address: %Ecto.Changeset{action: :insert, changes: %{street_address: "1234"}, 
    constraints: [], 

.... 

    model: %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded>, 
    address: %MyApp.Address{__meta__: #Ecto.Schema.Metadata<:loaded>, 
     assoc_id: 1229, id: 308, 
     street_address: "41423 Jakubowski Village" 
     .... 
    } 
    } 
} 

Errore: fisso come di Ecto v1.0.3 o poi

** (exit) an exception was raised: 
    ** (Postgrex.Error) ERROR (undefined_table): relation "abstract table: addresses" does not exist 
     (ecto) lib/ecto/adapters/sql.ex:479: Ecto.Adapters.SQL.model/6 
     (ecto) lib/ecto/repo/model.ex:219: Ecto.Repo.Model.apply/4 
     (ecto) lib/ecto/repo/model.ex:71: anonymous fn/10 in Ecto.Repo.Model.insert/4 
     (ecto) lib/ecto/repo/model.ex:340: anonymous fn/3 in Ecto.Repo.Model.wrap_in_transaction/8 
     (ecto) lib/ecto/adapters/sql.ex:531: anonymous fn/10 in Ecto.Adapters.SQL.transaction/3 
     (ecto) lib/ecto/pool.ex:262: Ecto.Pool.inner_transaction/3 
     (ecto) lib/ecto/adapters/sql.ex:534: Ecto.Adapters.SQL.transaction/3 
     (ecto) lib/ecto/association.ex:368: Ecto.Association.Has.on_repo_action/7 
+0

Sembra un errore in Ecto. Potete per favore compilare un rapporto? Presto avremo una nuova versione con correzioni. –

+0

Btw, non è necessario per verificare se 'is is_nil (user) do'. Usa 'Repo.get!' E sarà automaticamente qualcosa che viene visualizzato come 404. –

+0

Anche tu non hai bisogno di 'se changeset.valid? do'. 'update' restituirà' {: error, changeset} 'se il changeset non è valido. :) –

risposta

1

(Postgrex.Error) ERROR (undefined_table): relation "abstract table: addresses" does not exist era dovuto a un bug, che dovrebbe essere fissato in Ecto v1.0.3 o poi.


Il codice di cui sopra manca un ID per l'indirizzo, senza questo, Ecto inserirà una nuova risorsa invece di aggiornare uno esistente.

Request (PATCH):

{ 
    "user" => { 
    "address" => { 
     "id" => 4, 
     "street_address" => "1234" 
    } 
    } 
} 

Il nuovo codice di controllo, tra cui alcuni dei JoseValims suggerito miglioramenti:

def update(conn, %{"id" => id, "user" => params}) do 
    user = MyApp.Repo.get!(User, id) 
     |> MyApp.Repo.preload [:address 

    changeset = User.changeset(user, params) 
    case MyApp.Repo.update(changeset) do 
     {:ok, model} -> send_resp(conn, 204, "") 
     {:error, changeset} -> conn 
     |> put_status(:unprocessable_entity) 
     |> render(MyApp.ChangesetView, "error.json", changeset: changeset) 
    end 
    end 

Oppure, in questa situazione dal momento che l'indirizzo è sia necessario e has_one, il ID può essere aggiunto lato server:

def update(conn, %{"id" => id, "user" => params}) do 
    user = MyApp.Repo.get!(User, id) 
     |> MyApp.Repo.preload [:address] 

    address = params["address"] 
    address = Map.put address, "id", user.address.id 
    params = Map.put params, "address", address 

    changeset = User.changeset(user, params) 
    case MyApp.Repo.update(changeset) do 
     {:ok, model} -> send_resp(conn, 204, "") 
     {:error, changeset} -> conn 
     |> put_status(:unprocessable_entity) 
     |> render(MyApp.ChangesetView, "error.json", changeset: changeset) 
    end 
    end 
Problemi correlati