为什么 Ecto 的 `cast` 不能将整数转换为字符串?

Why does Ecto's `cast` not convert an integer to a string?

我有一个包含 field :owned_by_id, :string 的 Ecto 模式。我将该字段声明为字符串,因为我需要支持 "abc123" 之类的值以及“123”之类的值。

docs for cast/3 说:

The second argument is a map of params that are cast according to the type information from data.

在我的模块中,我将 changeset 定义为:

def changeset(struct, params \ %{}) do
  cast(struct, params, [:owned_by_id])
end

当我这样做时:

MyModule.changeset(%MyModule{}, %{owned_by_id: 1})

...我希望 cast 根据 field 声明将 owned_by_id 整数参数转换为字符串。

然而,我得到的是一个包含

的变更集
errors: [owned_by_id: {"is invalid", [type: :string]}]

我可以自己打电话给 Integer.to_string(1),但 cast 不应该处理吗?有没有办法让它自动处理这个问题?

虽然文档确实说参数是 "cast according to the type information",但 Ecto 没有为 Integer -> String 实现转换。我的猜测是,这是因为很少需要这样做,而字符串 -> 整数转换对于通过 Web 表单发送输入时非常有用,其中所有字段都以字符串形式到达。


如果需要这种转换,您可以创建自定义类型。该文档有一个实现类似内容的自定义类型的示例:https://github.com/elixir-ecto/ecto/blob/d40008db48ec26967b847c3661cbc0dbaf847454/lib/ecto/type.ex#L29-L40

您的类型看起来像:

def type, do: :string

def cast(integer) when is_integer(integer) do
  {:ok, Integer.to_string(integer)}
end
def cast(string) when is_binary(string), do: {:ok, string}
def cast(_), do: :error

...

注意:我不建议这样做。在我看来,显式转换会更简单,除非您要实现像我上面链接的文档示例那样复杂的东西。

如果你想要一个即插即用的解决方案,你可以使用我创建的这个十六进制包。 https://github.com/luizParreira/ecto_cast_to_string