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

时间:2019-07-20 10:33:37

标签: elixir ecto

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

attrs = payload_fields_to_payload(attrs)

问题在于传入的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

1 个答案:

答案 0 :(得分:1)

  

我只是重命名属性中的键。

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

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
  

问题是传入的attrs可以基于原子

允许这样做的问题是:如果attr映射具有1400万个原子键怎么办?繁荣!您的应用崩溃了。数百万个仅包含几个原子键的attr映射也可能发生相同的情况。这就是Phoenix为何在params映射中使用字符串键来获取表单数据的原因-这样做是为了防止攻击者通过发送数百万个具有唯一键的请求来泛滥原子表。