I have an Ecto schema that includes field :owned_by_id, :string
. I declared the field a string because I need to support values like "abc123" as well as values like "123".
The docs for cast/3 say:
The second argument is a map of params that are cast according to the type information from data
.
In my module, I define changeset
like:
def changeset(struct, params \\ %{}) do
cast(struct, params, [:owned_by_id])
end
When I do this:
MyModule.changeset(%MyModule{}, %{owned_by_id: 1})
... I would expect cast
to turn that owned_by_id
integer param into a string, based on the field
declaration.
However, what I get instead is a changeset that includes
errors: [owned_by_id: {"is invalid", [type: :string]}]
I could call Integer.to_string(1)
myself, but shouldn't cast
handle that? Is there a way to have it handle this automatically?
While the docs do say that the params are "cast according to the type information", Ecto does not implement casting for Integer -> String. My guess would be that's because this is rarely needed while the String -> Integer conversion is useful for when the input is sent via a web form where all the fields arrive as strings.
You can create a custom type if you want this kind of conversion. The documentation has an example of a custom type that implements something similar: https://github.com/elixir-ecto/ecto/blob/d40008db48ec26967b847c3661cbc0dbaf847454/lib/ecto/type.ex#L29-L40
Your type would look something like:
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
...
Note: I wouldn't recommend doing this. In my opinion, an explicit conversion would be simpler unless you're implementing something complex like the documentation example I've linked to above.
If you want a plug and play solution, you can use this hex package that I have created. https://github.com/luizParreira/ecto_cast_to_string