Nested Embedded Schema Changeset

Hi all, I have a nested embedded schema like this

embedded_schema do

embeds_many :chart, Chart do
  field(:vendor, :string)
  field(:type, :string)
  field(:background_color, :string)
  field(:border_color, :string)
  field(:plot_background_color, :string)
  field(:height, :string)
  field(:width, :string)      
end

end

I don’t know how to make the changeset for this.Please help.

You need to create the Chart module, which will contain… embedded_schema and changeset. Create the changeset as You would for any schema.

And in the master schema, use embeds_many, then use put_change on the master changeset

Ecto.Changeset.put_change(changeset, :charts, charts)
2 Likes

Inline embeds and their changesets are explained here: https://hexdocs.pm/ecto/Ecto.Schema.html#embeds_many/3-inline-embedded-schema

3 Likes

This might help you get your head around it. https://medium.com/@ItizAdz/ecto-changesets-put-cast-embeds-and-assocs-remember-the-difference-once-and-for-all-10781cc60857

In short the answer is you create them similar to how you would a usual assoc. In ecto there are associations (think has_one, has_many) and there are embeds (think embeds_one, embeds_many). Each of those has their own function for creating a changeset; cast_assoc and cast_embed. In your case you probably want a cast_embed

1 Like

Just to post the example from the documentation so that it’s immediately clear how to do this (I still had to spend some time finding the answer even after reading through this thread):

def changeset(schema, params) do
  schema
  |> cast(params, [:name])
  |> cast_embed(:child, with: &child_changeset/2)
end

defp child_changeset(schema, params) do
  schema
  |> cast(params, [:name, :age])
end

In short, you’ll need to call the cast_embed function and provide the changeset function defined for the embedded schema under the with: key.

2 Likes

Thanks for this info, but it doesn’t work for me.

schema "organizations" do
    field :email, :string
    field :name, :string
    field :slug, :string
    field :phone, :string
    timestamps()

    embeds_many :children, Child do
      field :name, :string
      field :age,  :integer
    end
end

def changeset(organization, attrs) do
    organization
    |> cast(attrs, [:name, :email, :phone, :slug])
    |> cast_embed(:children, with: &child_changeset/2) # or put_embed
    |> validate_required([:name, :slug])
  end

defp child_changeset(schema, params) do
    IO.puts("HELLO") # not printed
    schema
    |> cast(params, [:name, :age, :trigger]) # trigger not raised
  end
...

def new(conn, _params) do
    changeset = Company.change_organization(%Organization{})
    IO.inspect(changeset)  # Looks Wrong
    # #Ecto.Changeset<
        #action: nil,
       # changes: %{},
       # errors: [children: {"is invalid", [type: {:array, :map}]}],
        #data: #TurnStile.Company.Organization<>,
        #valid?: false
end

Why is the child changeset not getting combined properly?