Phoenix / Ecto - 更新embeds_many记录

时间:2017-07-21 21:37:09

标签: elixir phoenix-framework ecto

在我的凤凰应用中,我在尝试更新no function clause matching in Ecto.Changeset.change/2关系的模型时遇到embeds_many错误。我已经阅读了文档,并看到了其他相关内容,但我无法弄清楚我做错了什么。

首先,这是错误:

** (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

父母&#39;模型为Agenda,嵌入模型为AgendaPage。模型定义如下:

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

来自update的{​​{1}}行动

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 终端中,我可以使用iex访问现有议程,这会回复以下记录:

MyApp.Repo.get(MyApp.Agenda, "default_agenda")

将在控制器操作中传递给变更集的%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"}]} 示例如下所示:

agenda_params

但是尝试通过我的更新操作运行此数据会产生错误。任何人都可以提供一些指导吗?

1 个答案:

答案 0 :(得分:0)

首先,以下看起来不正确:

%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"}]}

pages列表应该是AgendaPage结构列表,而不是地图,如下面的示例所示。我不确定你是怎么做到的。

然而,主要问题似乎是在你的参数中提供id字段。尝试根据上面的示例参数更新现有记录时,我遇到了同样的问题。但是,如果你从更新中的params中删除id字段,它就可以工作。

这是一个有效的例子:

iex(4)> {:ok, ag} = Repo.insert Agenda.changeset(%Agenda{id: "default_agenda"}, %{name: "Default Agenda"})
[debug] QUERY OK db=9.7ms
INSERT INTO "agendas" ("id","name","inserted_at","updated_at") VALUES ($1,$2,$3,$4) ["default_agenda", "Default Agenda", {{2017, 7, 21}, {23, 43, 46, 739178}}, {{2017, 7, 21}, {23, 43, 46, 747000}}]
{:ok,
 %Playground.Demo.Agenda{__meta__: #Ecto.Schema.Metadata<:loaded, "agendas">,
  id: "default_agenda", inserted_at: ~N[2017-07-21 23:43:46.739178],
  name: "Default Agenda", pages: [],
  updated_at: ~N[2017-07-21 23:43:46.747000]}}
iex(5)> Repo.update Agenda.changeset(ag, %{
...(5)>   "name" => "Default Agenda",
...(5)>   "pages" => [
...(5)>     %{
...(5)>       "content" => "<p>foo</p>",
...(5)>       "duration" => 10,
...(5)>       "id" => "0849862a-0794-4466-88a3-6052da360ca0"
...(5)>     },
...(5)>     %{
...(5)>       "content" => "<p>bar</p>",
...(5)>       "duration" => 15,
...(5)>       "id" => "93387d2d-a6ed-4902-911f-4dc1525aca2b"
...(5)>     }
...(5)>   ]
...(5)> }
...(5)> )
[debug] QUERY OK db=15.7ms
UPDATE "agendas" SET "pages" = $1, "updated_at" = $2 WHERE "id" = $3 [[%{content: "<p>foo</p>", duration: 10, id: "e7b5b7d9-9308-43f0-8bc7-c5956640772b"}, %{content: "<p>bar</p>", duration: 15, id: "1c668d06-5c60-4a4d-a052-43520597162d"}], {{2017, 7, 21}, {23, 44, 23, 752892}}, "default_agenda"]
{:ok,
 %Playground.Demo.Agenda{__meta__: #Ecto.Schema.Metadata<:loaded, "agendas">,
  id: "default_agenda", inserted_at: ~N[2017-07-21 23:43:46.739178],
  name: "Default Agenda",
  pages: [%Playground.Demo.AgendaPage{content: "<p>foo</p>", duration: 10,
    id: "e7b5b7d9-9308-43f0-8bc7-c5956640772b"},
   %Playground.Demo.AgendaPage{content: "<p>bar</p>", duration: 15,
    id: "1c668d06-5c60-4a4d-a052-43520597162d"}],
  updated_at: ~N[2017-07-21 23:44:23.752892]}}
iex(6)> Repo.get Agenda, "default_agenda"
[debug] QUERY OK source="agendas" db=7.2ms
SELECT a0."id", a0."name", a0."pages", a0."inserted_at", a0."updated_at" FROM "agendas" AS a0 WHERE (a0."id" = $1) ["default_agenda"]
%Playground.Demo.Agenda{__meta__: #Ecto.Schema.Metadata<:loaded, "agendas">,
 id: "default_agenda", inserted_at: ~N[2017-07-21 23:43:46.739178],
 name: "Default Agenda",
 pages: [%Playground.Demo.AgendaPage{content: "<p>foo</p>", duration: 10,
   id: "e7b5b7d9-9308-43f0-8bc7-c5956640772b"},
  %Playground.Demo.AgendaPage{content: "<p>bar</p>", duration: 15,
   id: "1c668d06-5c60-4a4d-a052-43520597162d"}],
 updated_at: ~N[2017-07-21 23:44:23.752892]}
iex(7)>