【问题标题】:Ecto - updating nested embedEcto - 更新嵌套嵌入
【发布时间】:2016-10-28 16:24:24
【问题描述】:

我无法使用 ecto 更新嵌套设置,我要么得到“无更改”变更集,要么出现错误。迁移:

def change do
  create table(:trees) do
  ...
  add :settings, :map

设置如下:

defmodule Final.TreeSettings do
  use Ecto.Schema

  embedded_schema do
    ...
    field :columns, :map       
    timestamps
  end
end

注意嵌套列映射。

我可以像这样轻松插入新的树行:

changeset = Tree.changeset(%Tree{}, %{user_id: user_id, name: x})
      |> Ecto.Changeset.put_embed(:settings, treeSettings)

但是以同样的方式更新它是行不通的:

get_tree = Repo.one! from p in Tree, where: p.name == ^tree["name"], where: p.user_id == ^user_id
settingss = get_tree.settings
settingss = Kernel.update_in(settingss.columns[tree["setting"]][tree["type"]], fn x -> "asdasd" end)
# IO.inspect(settingss) shows correct changes here.
changeset =
    get_tree
    |> Ecto.Changeset.change
    |> Ecto.Changeset.put_embed(:settings, settingss)    
    IO.inspect changeset

给:

#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #Final.Tree<>,
 valid?: true>

【问题讨论】:

    标签: elixir ecto


    【解决方案1】:

    我认为您可以将嵌入包装到 Ecto.Changeset.change/2 中,然后再将其用于 Ecto.Changeset.html#put_embed/4

    完整示例:

    迁移:

    defmodule Final.Repo.Migrations.CreateTree do
      use Ecto.Migration
    
      def change do
        create table(:trees) do
          add :settings, :map
        end
      end
    end
    

    模型树:

    defmodule Final.Tree do
      use Final.Web, :model
    
      schema "trees" do
        embeds_one :settings, Final.TreeSettings
      end
    
      def changeset(struct, params \\ %{}) do
        struct
        |> cast(params, [])
        |> cast_embed(:settings)
      end
    end
    

    模型树设置:

    defmodule Final.TreeSettings do
      use Final.Web, :model
    
      embedded_schema do
        field :columns, :map
      end
    
      def changeset(struct, params \\ %{}) do
        struct
        |> cast(params, [:columns])
      end
    end
    

    测试:

    defmodule Final.TreeTest do
      use Final.ModelCase
    
      alias Final.Tree
    
      test "updating nested embed" do
        Repo.insert! Tree.changeset(%Tree{}, %{settings: %{columns: %{"key" => "value", "key2" => "value2"}}})
    
        tree = Repo.one(Tree)
        settings_changeset = tree.settings
        |> Ecto.Changeset.change(%{columns: %{tree.settings.columns | "key" => "new value"}})
    
        changeset = tree
        |> Ecto.Changeset.change
        |> Ecto.Changeset.put_embed(:settings, settings_changeset)
        Repo.update! changeset
    
        assert Repo.one(Tree).settings.columns == %{"key" => "new value", "key2" => "value2"}
      end
    end
    

    【讨论】:

    • 据我了解,这是因为Nema Ga 在他的settingss 中有相同的键(主键),所以它应该像更新一样工作,但更新只能通过 %Ecto.Changeset{} 工作。因此,如果他将删除密钥并使用on_replace: :destroy,那么此代码将起作用。我的假设正确吗?
    • 我相信选项是on_replace: :delete,而不是:destroy。 Ecto 说:':on_replace' option for :pages. The only valid options are: ':raise', ':mark_as_invalid', ':delete'
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多