如何在使用前有效地重命名 ecto 变更集中的属性

How to efficiently rename an attribute in an ecto changeset before it's being used

我想出了以下解决方案,我在演员表之前调用了它:

attrs = payload_fields_to_payload(attrs)

问题是传入的属性可以是基于原子的,但不一定是。那么有没有比我在这里做的更快或更清洁的方法?我只是重命名属性中的一个键。这个任务似乎有很多代码。

def payload_fields_to_payload(attrs) do
    attrs = cond do
      Map.has_key?(attrs, "payload_fields") ->
        Map.put(attrs, "payload", Map.get(attrs, "payload_fields"))
      Map.has_key?(attrs, :payload_fields) ->
        Map.put(attrs, :payload, Map.get(attrs, :payload_fields))
      true -> attrs
    end
    attrs
end

I'm just renaming a key in the attributes.

这不是您的代码实际执行的操作 - 您的代码 添加 地图的新键:

defmodule A do
  def go do

    attrs_list = [
      %{"payload_fields" => "hello", type: "ABC"},
      %{payload_fields: "goodbye", type: "XYZ"},
      %{abc: "dog", xyz: "cat"}
    ]

    Enum.map(attrs_list, fn attrs -> payload_fields_to_payload(attrs) end)

  end

  def payload_fields_to_payload(attrs) do
    cond do
      Map.has_key?(attrs, "payload_fields") ->
        Map.put(attrs, "payload", Map.get(attrs, "payload_fields"))
      Map.has_key?(attrs, :payload_fields) ->
        Map.put(attrs, :payload, Map.get(attrs, :payload_fields))
      true -> attrs
    end
  end

end

输出:

iex(1)> A.go           |                         |
[                      V                         V
  %{:type => "ABC", "payload" => "hello", "payload_fields" => "hello"},
  %{payload: "goodbye", payload_fields: "goodbye", type: "XYZ"},
  %{abc: "dog", xyz: "cat"}
]

但是,如果旧密钥被 cast() 过滤掉,那就没什么大不了的了。

我会使用模式匹配和多个函数子句,而不是使用函数体内的逻辑来确定要做什么。以下解决方案将键 payload_fields 替换为键 payload:

defmodule A do

  def go do

    attrs_list = [
      %{"payload_fields" => "hello", type: "ABC"},
      %{payload_fields: "goodbye", type: "XYZ"},
      %{abc: "dog", xyz: "cat"}
    ]

    Enum.map(attrs_list, fn attrs -> convert_key(attrs) end)

  end

  def convert_key(%{"payload_fields" => value}=map) do  #string key
    map
    |> Map.delete("payload_fields")
    |> Map.put("payload", value)
  end
  def convert_key(%{payload_fields: value}=map) do  # atom key
    map
    |> Map.delete(:payload_fields)
    |> Map.put(:payload, value)
  end
  def convert_key(map), do: map

end

输出:

iex(1)> A.go
[
  %{:type => "ABC", "payload" => "hello"},
  %{payload: "goodbye", type: "XYZ"},
  %{abc: "dog", xyz: "cat"}
]

如果您真的想向地图添加新键——而不是重命名键——代码简化为:

  def convert_key(%{"payload_fields" => value}=map) do
    Map.put(map, "payload", value)
  end
  def convert_key(%{payload_fields: value}=map) do
    Map.put(map, :payload, value)
  end
  def convert_key(map), do: map

The issue was that the incoming attrs can be atom-based

允许这样做的问题是:如果 attr 映射有 1400 万个原子键怎么办?繁荣!你的应用程序崩溃了。同样的事情也会发生在数百万个 attr 映射中,每个映射只包含几个原子键。这就是 Phoenix 在 params 映射中使用字符串键作为表单数据的原因——这样做可以防止攻击者通过发送带有唯一键的数百万个请求来淹没原子 table。