Cast_embed fails with deeply nested inline embeds

When I put an embeds_one inside an embeds_one then cast_embed fails:

iex(n)> MyApp.DeeplyNested.run
#Ecto.Changeset<
  action: nil,
  changes: %{
    child: #Ecto.Changeset<
      action: :insert,
      changes: %{child_name: "child"},
      errors: [],
      data: #MyApp.DeeplyNested.Child<>,
      valid?: true
    >,
    parent_name: "parent"
  },
  errors: [],
  data: #MyApp.DeeplyNested<>,
  valid?: true
>
** (ArgumentError) cannot cast embed `grant_child`, assoc `grant_child` not found. Make sure it is spelled correctly and properly pluralized (or singularized)
    (ecto) lib/ecto/changeset.ex:841: Ecto.Changeset.relation!/4
    (ecto) lib/ecto/changeset.ex:759: Ecto.Changeset.cast_relation/4

defmodule MyApp.DeeplyNested do
  use Ecto.Schema
  import Ecto.Changeset
  alias __MODULE__

  @primary_key false
  embedded_schema do
    field :parent_name, :string
    embeds_one :child, Child, primary_key: false do
      field :child_name, :string
      embeds_one :grant_child, GrantChild, primary_key: false do
        field :grant_child_name, :string
      end
    end
  end

  def changeset(%DeeplyNested{} = struct, %{} = params) do
    ok_result = struct
    |> cast(params, [:parent_name])
    |> cast_embed(:child, required: true, with: &child_changeset/2)
    |> IO.inspect
    # this will fail...
    ok_result
    |> cast_embed(:grant_child, required: true, with: &grant_child_changeset/2)
  end

  defp child_changeset(struct, params) do
    struct
    |> cast(params, [:child_name])
    |> validate_required([:child_name])
  end

  defp grant_child_changeset(struct, params) do
    struct
    |> cast(params, [:grant_child_name])
    |> validate_required([:grant_child_name])
  end

  def run do
    params = %{
      parent_name: "parent",
      child: %{
        child_name: "child",
        grant_child: %{
          grant_child_name: "grant_child"
        }
      }
    }
    changeset(__struct__(), params)
  end
  
end

Is this a known limitation? Does someone maybe have a workaround?

Taking a quick look, that needs to go into the child_changeset. grant_child does not exist in the top level of the schema. The child will need to do the cast embed.

1 Like

Awesome! you’re right, tnx!