【问题标题】:Phoenix/Ecto - changeset error when updating embeds_many recordPhoenix/Ecto - 更新 embeds_many 记录时的变更集错误
【发布时间】:2017-07-21 21:37:08
【问题描述】:

在我的 Phoenix 应用程序中,我在尝试更新具有 embeds_many 关系的模型时收到 no function clause matching in Ecto.Changeset.change/2 错误。我已经阅读了文档并查看了有关此的其他帖子,但我无法弄清楚我做错了什么。

首先,这是错误:

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

“父”模型是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

还有来自agenda_controller.exupdate 操作

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 的示例将被传递到控制器操作中的变更集中,如下所示:

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

但尝试通过我的更新操作运行此数据会产生错误。谁能提供一些指导?

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

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

    %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 字段。尝试根据您上面的示例参数更新现有记录时,我遇到了同样的问题。但是,如果您从更新的参数中删除 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)>
    

    【讨论】:

    • 是的,你是对的 - 架构似乎有问题。我需要回溯一点,会及时通知你。我对id 问题有预感,但不确定。史蒂夫,你这个男人!
    • 史蒂夫,你给我指出了正确的方向,但我仍然需要弄清楚如何解决它。关键在于,当我得到插入的记录时,它并没有保留对嵌入式模型的引用——我得到的是pages: [%{"content"...而不是pages: [%MyApp.AgendaPage{"content"...id 问题也是一个问题,但这是主要问题。我实际上没有使用 Postgres - 使用实验性 DynamoDB 适配器......所以需要弄清楚如何保留该结构。
    • 我能够通过手动将模型引用添加到传递给变更集的第一个参数来成功更新。史蒂夫,谢谢你的观点!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    相关资源
    最近更新 更多