Ecto.Changeset.put_embed/4 with struct is being ignored

From Ecto.Changeset ā€” Ecto v3.11.1

The given value may either be the embed struct, a changeset for the given embed or a map or keyword list of changes to be applied to the current embed. On all cases, it is expected the keys to be atoms. If a map or keyword list are given and there is no embed, one will be created.

However, when I pass the whole struct as value (ecto 2.2.6), it just ignores the value. Am I interpreting the documentation incorrectly? Or is it really a bug in Ecto?

(Note: this is for Zendesk API :slight_smile: )

defmodule Organization do
  use Ecto.Schema

  @primary_key {:id, :integer, autogenerate: false}
  embedded_schema do
    field(:name, :string)
    embeds_one(:organization_fields, OrganizationFields, on_replace: :update)
  end
end

defmodule OrganizationFields do
  use Ecto.Schema

  alias Ecto.Changeset

  @primary_key false
  embedded_schema do
    field(:key1, :string)
    field(:key2, :string)
  end
end
defmodule EctoEmbedTest do
  use ExUnit.Case

  alias Ecto.Changeset

  @existing_struct %Organization{
    id: 1234,
    name: "foo",
    organization_fields: %OrganizationFields{key1: "foo", key2: "bar"}
  }
  @new_fields_map %{key1: "bar", key2: "bar"}
  @new_fields_struct struct!(OrganizationFields, @new_fields_map)

  test "put_embed with changeset" do
    changeset =
      @existing_struct
      |> Ecto.Changeset.change()
      |> Ecto.Changeset.put_embed(
           :organization_fields,
           Changeset.change(@existing_struct.organization_fields, @new_fields_map)
         )

    %Changeset{changes: %{organization_fields: %Changeset{changes: org_changes}}} = changeset
    assert %{key1: "bar"} == org_changes
  end

  test "put_embed with map" do
    changeset =
      @existing_struct
      |> Changeset.change()
      |> Changeset.put_embed(:organization_fields, @new_fields_map)

    %Changeset{changes: %{organization_fields: %Changeset{changes: org_changes}}} = changeset
    assert %{key1: "bar"} == org_changes
  end

  test "put_embed with struct" do
    changeset =
      @existing_struct
      |> Ecto.Changeset.change()
      |> Ecto.Changeset.put_embed(
           :organization_fields,
           @new_fields_struct
         )

    %Changeset{changes: %{organization_fields: %Changeset{changes: org_changes}}} = changeset
    assert %{key1: "bar"} == org_changes
  end
end

All tests passed except put_embed with struct, which gives empty changes.

Note: I found two related topics in this forum, and both suggested to pass changeset.

2 Likes

@chulkilee I realize this is an old post but Iā€™m running into this as well on Ecto 3.1. Did you ever figure out a solution?

1 Like

I ended up to build a map from a struct, and pass it to embed schema - so not putting struct.