Ecto - Updating Nested (Polymorphic) Associations

How to update a model with nested communication (using [Elixir, Phoenix, Ecto])?

I tried the following to process it as part of its parent update without success (using blog> platformatec as inspiration).

Models:

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 

Changes (from logs):

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

Error: Fixed with Ecto v1.0.3 or later

 ** (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 
+5
source share
1 answer

(Postgrex.Error) ERROR (undefined_table): relation "abstract table: addresses" does not exist was caused by an error that should be fixed in Ecto v1.0.3 or later.


In the above code, there is no identifier for the address, without this Ecto will insert a new resource instead of updating an existing one.

Request (PATCH):

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

The new controller code, including some of JoseValims, has suggested improvements:

  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 

Or, in this situation, since the address is required and has_one , an identifier can be added on the server side:

  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 
+1
source

All Articles