在Elixir Phoenix中动态创建JSON-api关系

时间:2016-10-16 15:36:24

标签: elixir phoenix-framework ecto

我们有很多测试我们手动建立关系。我们做的事情如下:

defp build_relationships(relationship_map, user) do
  relationship_map |> Map.put(:user, %{data: %{id: user.id}})
end

这很好用,但我想让它灵活接受任何类型的记录(不仅仅是用户)。作为参考,user创建时ex-machinainsert(:user)

有没有办法获取传入的type记录?如果我能得到一个标有"user"的字符串,我可以使用String.to_atom(param)并将其传递给Map.put,但我无法找到一种优雅的方式来执行此操作。

真正的问题是我如何获取user之类的记录并返回:user的原子?

感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

您可以使用.__struct__.__schema__/1获取有关Ecto结构的一些信息,例如:

iex(1)> %Post{}.__struct__.__schema__(:source)
"posts"

但由于从Schema到ExMachina工厂没有1:1的映射,我可以想到3种可能的方式:

  1. 将工厂重命名为使用复数形式,与表名相同,并使用.__struct__.__schema__(:source)获取名称。这将使您的工厂代码有点奇怪,因为您将使用insert(:users)插入一个用户。

  2. 继续使用单数名称,但使用一些库将多个表名转换为单数名,例如将"users"转换为"user""posts"转换为"post"的内容。

  3. 保留一份Schema< - >列表工厂名称映射,像这样(或者您甚至可以将映射移动到单独的函数):

    defp build_relationships(relationship_map, struct) do
      mapping = %{MyApp.User => :user, MyApp.Post => :post} # add more here
      relationship_map |> Map.put(mapping[struct.__struct__], %{data: %{id: struct.id}})
    end
    
  4. 使用Module.split/1获取模型的名称,并使用Macro.underscore/1将其转换为小写+下划线名称,然后使用String.to_existing_atom

    defp build_relationships(relationship_map, struct) do
      name = struct.__struct__ |> Module.split |> List.last |> Macro.underscore |> String.to_existing_atom
      relationship_map |> Map.put(name, %{data: %{id: struct.id}})
    end
    

    如果您使用嵌套模块作为模型,这将无法正常工作,例如MyApp.Something.User

  5. 如果所有模型都处于相同的嵌套级别,我会选择4,如果你想更明确,我会选择3。