In my Phoenix app, I am getting a no function clause matching in Ecto.Changeset.change/2
error when trying to update a model with an embeds_many
relationship. I've read the docs and seen other posts about this, but I can't figure out what I'm doing wrong.
First off, here's the error:
** (FunctionClauseError) no function clause matching in Ecto.Changeset.change/2
(ecto) lib/ecto/changeset.ex:307: Ecto.Changeset.change(%{"content" => "<p>Nice to see you</p>", "duration" => 15, "id" => "93387d2d-a6ed-4902-911f-4dc1525aca2b"}, %{})
(ecto) lib/ecto/changeset/relation.ex:196: Ecto.Changeset.Relation.on_replace/2
(ecto) lib/ecto/changeset/relation.ex:299: Ecto.Changeset.Relation.reduce_delete_changesets/5
(ecto) lib/ecto/changeset.ex:691: Ecto.Changeset.cast_relation/4
(myapp) web/models/agenda.ex:20: MyApp.Agenda.changeset/2
The 'parent' model is Agenda
, and the embedded model is AgendaPage
. The models are defined as follows:
agenda.ex
defmodule MyApp.Agenda do
use MyApp.Web, :model
@primary_key {:id, :string, []}
@derive {Phoenix.Param, key: :id}
schema "agenda" do
field :name, :string
embeds_many :pages, MyApp.AgendaPage, on_replace: :delete
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name])
|> cast_embed(:pages)
|> validate_required([:name])
end
end
agenda_page.ex
defmodule MyApp.AgendaPage do
use MyApp.Web, :model
embedded_schema do
field :content, :string
field :duration, :integer
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:content, :duration])
end
end
And the update
action from agenda_controller.ex
def update(conn, %{"id" => id, "agenda" => agenda_params}) do
agenda = Repo.get!(Agenda, id)
changeset = Agenda.changeset(agenda, agenda_params)
case Repo.update(changeset) do
{:ok, agenda} ->
json conn, %{status: "ok", agenda: agenda}
{:error, changeset} ->
errors = parse_errors(changeset)
IO.inspect errors
json(conn |> put_status(400), %{status: "error", message: "Failed to update Agenda", errors: errors})
end
end
In the iex
terminal, I can access an existing agenda with MyApp.Repo.get(MyApp.Agenda, "default_agenda")
, which gives back the following record:
%MyApp.Agenda{__meta__: #Ecto.Schema.Metadata<:built, "agenda">,
id: "default_agenda", name: "Default Agenda",
pages: [%{"content" => "<p>This is the default agenda</p>", "duration" => 10,
"id" => "0849862a-0794-4466-88a3-6052da360ca0"},
%{"content" => "<p>Nice to see you</p>", "duration" => 15,
"id" => "93387d2d-a6ed-4902-911f-4dc1525aca2b"}]}
An example of the agenda_params
that would be passed into the changeset in the controller action would look like:
%{
"id" => "default_agenda",
"name" => "Default Agenda",
"pages" => [
%{
"content" => "<p>foo</p>",
"duration" => 10,
"id" => "0849862a-0794-4466-88a3-6052da360ca0"
},
%{
"content" => "<p>bar</p>",
"duration" => 15,
"id" => "93387d2d-a6ed-4902-911f-4dc1525aca2b"
}
]
}
But trying to run this data through my update action produces the error. Can anyone offer some guidance?