Ecto put_embed's doesn't register changes when keys change

I am trying to make sense of this for the last 2 hours, but am unable to make any progress :icon_sad:

I have an embeds_many setup similar to the one shown in the Ecto hexdocs:

defmodule Order do
  use Ecto.Schema

  schema "orders" do
    embeds_many :items, Item
  end
end

defmodule Item do
  use Ecto.Schema

  embedded_schema do
    field :name,
    filed :quantity
  end
end

When I try to update the embedded records using the following code, it doesn’t register any changes. For some reason ecto thinks that nothing was changed.

alias PhxDemo.{Repo,Order,Item}
Repo.insert! %Order{ items: [%Item{quantity: 10, name: "Strings" }]}
[debug] QUERY OK db=10.7ms
INSERT INTO "orders" ("items","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" [[%{id: "a766619c-2974-4c3c-80ce-b57768fac754", name: "Strings", quantity: 10}], {{2017, 6, 20}, {10, 36, 54, 854271}}, {{2017, 6, 20}, {10, 36, 54, 854279}}]
%PhxDemo.Order{__meta__: #Ecto.Schema.Metadata<:loaded, "orders">, id: 2,
 inserted_at: ~N[2017-06-20 10:36:54.854271],
 items: [%PhxDemo.Item{id: "a766619c-2974-4c3c-80ce-b57768fac754",
   name: "Strings", quantity: 10}], updated_at: ~N[2017-06-20 10:36:54.854279]}
iex(5)> o = Repo.get(Order, 2)
[debug] QUERY OK source="orders" db=3.8ms
SELECT o0."id", o0."items", o0."inserted_at", o0."updated_at" FROM "orders" AS o0 WHERE (o0."id" = $1) [2]
%PhxDemo.Order{__meta__: #Ecto.Schema.Metadata<:loaded, "orders">, id: 2,
 inserted_at: ~N[2017-06-20 10:36:54.854271],
 items: [%PhxDemo.Item{id: "a766619c-2974-4c3c-80ce-b57768fac754",
   name: "Strings", quantity: 10}], updated_at: ~N[2017-06-20 10:36:54.854279]}
iex(6)> o.items
[%PhxDemo.Item{id: "a766619c-2974-4c3c-80ce-b57768fac754", name: "Strings",
  quantity: 10}]
iex(7)> import Ecto.Changeset
iex(8)> change(o) |> put_embed(:items, [ %{ hd(o.items) | quantity: 30 } ])
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #PhxDemo.Order<>,
 valid?: true>
iex(9)> 

Would really appreciate any help

@michalmuskala Helped me with this on slack. The fix for this was to use changesets while using put_embed. So, the following change fixed the issue.

import Ecto.Changeset
item_changeset = hd(o.items) |> change(quantity: 30)
change(o) |> put_embed(:items, [item_changeset])
4 Likes