【问题标题】:Make changes to a cached model with associations使用关联更改缓存模型
【发布时间】:2016-08-16 01:04:12
【问题描述】:

我有一个从 Repo 获取的模型并预加载了一些关联。我在 GenServer 中维护这个大型结构,但对它进行持久更改。我怎样才能坚持这些变化?例如:

query = from e in Empire,
        where: e.user_id == ^user.id,
        preload: [:board, {:board, [{:tiles, [:system]}]}]
empire = Repo.one(query)
#=> %Empire{
  id: 1,
  currency: 1000,
  board: %Board{
    empire_id: 1,
    tiles: [
      %Tile{
        id: 1,
        system: %System{
          id: 1,
          tile_id: 1,
          ore: 10
        }
      },
      %Tile{
        id: 2,
        system: nil
      }
    ]
  }
}

changed_empire = Empire.move_system(empire)
#=> %Empire{
  id: 1,
  currency: 500, #changed
  board: %Board{
    empire_id: 1,
    tiles: [
      %Tile{
        id: 1,
        system: nil #changed
        }
      },
      %Tile{
        id: 2,
        system: %System{
          id: 1,
          tile_id: 2, #changed
          ore: 5 #changed
      }
    ]
  }
}

请注意,系统已从一个 Tile 移动到另一个 Tile,并且 Empires 货币发生了变化。更改可以包括添加或删除关联。所有这些都是在没有来自客户端的任何数据的情况下完成的,因此无需担心添加错误数据,因为服务器是如何操作模型的唯一权威。

我不能依赖正常

changeset(model, params \\ %{}) do
  model
  |> cast(params, [])
end

因为 params 将是结构体。我可以尝试使用建议的更改

update_change(struct) do
  struct
  |> change()
  |> validate()
end

但现在我遇到了需要手动将关联结构发送到它们自己的 update_change 函数的问题。

有没有更好的方法来完成这个而不增加我似乎遇到的所有额外复杂性?

【问题讨论】:

  • 你可以试试Ecto.Changeset.change/2hexdocs.pm/ecto/Ecto.Changeset.html#change/2
  • 我仍然想进行验证。 change/2 似乎跳过了它们。
  • 第二个参数不能是struct,而是params的map。如果你有一个结构并且想要转换它, Map.from_struct 应该可以解决问题。
  • @ChristianDiLorenzo 我正在考虑这个。唯一的问题是我需要通过 Map.from_struct 遍历每个关联。但到目前为止,这似乎是最简单的方法。
  • 别担心,它没有副作用。 :-)

标签: elixir phoenix-framework ecto


【解决方案1】:

在 cmets 的帮助下,我能够解决我的问题。我创建了一个小助手模块,它遍历所有数据并在其上调用 Map.from_struct。这与常规 cast/3 相结合,得到了我期望的结果。

这是助手:

defmodule RepoHelper do
  def from_struct(struct) when is_map(struct) do
    from(struct)
  end

  defp from(struct = %{__struct__: _}) do
    Enum.reduce(Map.to_list(struct), struct, fn {key, val}, acc ->
      Map.put(acc, key, from(val))
    end)
    |> Map.from_struct()
  end

  defp from(map) when is_map(map) do
    Enum.reduce Map.to_list(map), map, fn {key, val}, acc ->
      Map.put(acc, key, from(val))
    end
  end

  defp from(list) when is_list(list) do
    Enum.map list, fn item -> from(item) end
  end

  defp from(value), do: value
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多