我是凤凰城/ Elixir的新手,我正试图绕过变更集。
我知道它包含一组用于创建或更新模型的更改。
我想知道的是在将更改推送到数据库之前是否以及如何修改更改。
我的用例如下:
由于不变性约束,我甚至不确定是否可以直接修改变更集,但我可以创建另一个变更集以插入仓库。
欢迎提出任何建议,并毫不犹豫地指出我可能正在做的不良做法或愚蠢的事情!
编辑以下评论: 我正在寻找类似的东西:
defp put_specialty_array(changeset) do
case changeset do
%Ecto.Changeset{valid?: true, changes: %{specialty: spec}} ->
put_change(changeset, :specialty, String.split(spec, ","))
_ ->
changeset
end
end
答案 0 :(得分:11)
我相信你要找的是一个自定义的Ecto.Type。我一直这样做,效果很好!它看起来像这样:
defmodule MyApp.Tags do
@behaviour Ecto.Type
def type, do: {:array, :string}
def cast(nil), do: {:ok, nil} # if nil is valid to you
def cast(str) when is_binary(str) do
str
|> String.replace(~r/\s/, "") # remove all whitespace
|> String.split(",")
|> cast
end
def cast(arr) when is_list(arr) do
if Enum.all?(arr, &String.valid?/1), do: {:ok, arr}, else: :error
end
def cast(_), do: :error
def dump(val) when is_list(val), do: {:ok, val}
def dump(_), do: :error
def load(val) when is_list(val), do: {:ok, val}
def load(_), do: :error
end
然后在迁移中添加一个类型正确的列
add :tags, {:array, :string}
最后在您的架构中指定您创建的字段类型。
field :tags, MyApp.Tags
然后您可以将其添加为变更集中的字段。如果您的类型的强制转换返回:error
,则变更集将出现类似{:tags, ["is invalid"]}
的错误。您不必担心模型或控制器中对该字段的任何处理。如果用户为该值发布字符串数组或仅使用逗号分隔的字符串,则它将起作用。
如果需要以不同的格式将值保存到数据库,只需更改def type
的返回值,并确保def dump
返回该类型的值,并{{1}可以将该类型的值读取到您想要的任何内部表示。一种常见的模式是为内部表示定义一个结构,以便您可以自己实现Poison的def load
甚至可以返回一个简单的字符串。一个示例可能是在json中编码为to_json
的LatLng类型,在postgres中存储为某种GIS类型,但是具有类似12.12345N,123.12345W
的结构,可以让您在elixir中进行一些简单的数学运算。 DateTime格式的工作方式非常类似(有一个elixir结构,db驱动程序的元组格式和json的ISO格式)。
我认为这对密码字段非常有用,顺便说一句。您可以压缩JSON表示,使用结构来表示算法,算法的参数将哈希与盐分开,或者其他任何使生活变得容易的事情。在您的代码中,要更新密码,只需%LatLng{lat: 12.12345, lng: -123.12345}
。
我意识到这是一个6个月大的问题,你可能已经找到了一些东西,但我最终来自谷歌搜索,并假设其他人也会这样做。