How do I use Ecto cast_embed correctly on this Map?

I’m trying to figure out how to use cast_embed but I don’t understand what I’m doing wrong.

defmodule Operation do
  use Ecto.Schema

  embedded_schema do
    field :amount, :integer
    field :type, :string
    embeds_one :item, Item
  end
end

defmodule Item do
  use Ecto.Schema

  embedded_schema do
    field :item_code, :string
    field :item_name, :string
  end
end

with the following data

{ 
  "item": {
    "item_code": "A1",
    "item_name": "NameA1"
  },
  "type": "type",
  "amount": 4
}

d = %{
  "amount" => 4,
  "item" => %{"item_code" => "A1", "item_name" => "NameA1"},
  "type" => "type"
}

cast(%Operation{}, d, [:amount, :type]) |> validate_required([:amount, :type]) |> cast_embed(:item)

This throws an error:

** (UndefinedFunctionError) function Item.changeset/2 is undefined or private. Did you mean one of:

  * __changeset__/0

Item.changeset(%Item{id: nil, item_code: nil, item_name: nil}, %{"item_code" => "A1", "item_name" => "NameA1"})
(ecto) lib/ecto/changeset.ex:736: anonymous fn/4 in Ecto.Changeset.on_cast_default/2
(ecto) lib/ecto/changeset/relation.ex:111: Ecto.Changeset.Relation.do_cast/5
(ecto) lib/ecto/changeset/relation.ex:292: Ecto.Changeset.Relation.single_change/5
(ecto) lib/ecto/changeset.ex:715: Ecto.Changeset.cast_relation/4

Been stuck on this for a while and I can’t find a working example of how to do this online, any ideas what I’m doing wrong here?

Take a look at the documentation for cast_embed

Specifically, note the :with option which states

:with - the function to build the changeset from params. Defaults to the changeset/2 function in the embed module

Basically, when you call cast_embed, it implicitly calls Item.changeset/2, but since you don’t have one defined its erroring out. You want to add a def changeset(changeset, params) function in your Item module

1 Like

Thanks, can you give an example of how that would be done with my above structs?
I’m still trying to get it working but frustratingly just getting errors…

This post is a cast_assoc example but should apply to cast_embed. Check it out:

Best regards,

1 Like

you want something like

defmodule Item do
  use Ecto.Schema

  embedded_schema do
    field :item_code, :string
    field :item_name, :string
  end

  def changeset(schema, params) do
    schema
    |> cast(params, [:item_code, :item_name])
  end
end
2 Likes

Many thanks, that helped immensely and I get the gist of how it works now!