5

Wie funktioniert ein Update ein Modell mit einer verschachtelten Vereinigung (mit [Elixir, Phoenix, Ecto])?Ecto - Aktualisierung verschachtelt (Polymorphe) Verbände

Ich habe Folgendes versucht, um es als Teil seiner Eltern Update zu behandeln, ohne Erfolg (mit der platformatec blog als Inspiration).

Modelle:

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 (von Stamm):

%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" 
     .... 
    } 
    } 
} 

Fehler: Feste wie von Ecto v1.0.3 oder später

** (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

Das klingt wie ein Fehler in Ecto. Können Sie bitte einen Bericht ausfüllen? Wir werden bald eine neue Version mit Fixes haben. –

+0

Btw, du brauchst das nicht um nach 'if is_nil (user) do 'zu suchen. Verwenden Sie 'Repo.get!' Und es wird automatisch etwas, das als 404 gerendert wird. –

+0

Sie brauchen auch nicht 'if changeset.valid? mach es. 'update' gibt' {: error, changeset} 'zurück, wenn das Changeset ungültig ist. :) –

Antwort

1

(Postgrex.Error) ERROR (undefined_table): relation "abstract table: addresses" does not exist war aufgrund eines Fehlers, der in Ecto v1.0.3 oder später behoben werden sollte.


Der obige Code ist eine ID für die Adresse, ohne dass dies fehlt, wird Ecto eine neue Ressource einfügen, anstatt eine vorhandene aktualisieren.

Request (PATCH):

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

Der neue Code-Controller, einige JoseValims einschließlich Verbesserungen vorgeschlagen:

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 

Oder in dieser Situation, da die Adresse beide erforderlich und has_one, die ID kann hinzugefügt werden Server-Seite:

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