jacklin
jacklin

Reputation: 2779

Custom Types in Elixir

I have a toy project I'm using to help me learn Elixir/Phoenix.

I'm importing some data from a csv and I want to insert those records into the database if they are valid according to my changeset.

The question I have is that sometimes one of the columns that normally contains an integer will have "n/a". This causes the changeset to be invalid, as it should.

I'm not sure what the Elixir standard way to handle this is.

In these cases I just want the "n/a" to be converted to 0.

Would you normally write a custom type for this that does that conversion?

https://hexdocs.pm/ecto/Ecto.Type.html

In Rails I might solve this on the model using a custom setter or in a before_save or something.

Upvotes: 2

Views: 938

Answers (1)

Dogbert
Dogbert

Reputation: 222040

I would do this in the changeset function of the model by "fixing" params before it's sent to cast for validation.

Example Model with a :count integer field:

def changeset(struct, params \\ %{}) do
  struct
  |> cast(fix_params(params), [:count])
  |> validate_required([:count])
end

defp fix_params(%{count: "n/a"} = params), do: %{params | count: 0}
defp fix_params(params), do: params

Demo:

iex(1)> Counter.changeset(%Counter{}, %{count: 123})
#Ecto.Changeset<action: nil, changes: %{count: 123}, errors: [],
 data: #MyApp.Counter<>, valid?: true>
iex(2)> Counter.changeset(%Counter{}, %{count: "n/a"})
#Ecto.Changeset<action: nil, changes: %{count: 0}, errors: [],
 data: #MyApp.Counter<>, valid?: true>
iex(3)> Counter.changeset(%Counter{}, %{count: "foo"})
#Ecto.Changeset<action: nil, changes: %{},
 errors: [count: {"is invalid", [type: :integer]}], data: #MyApp.Counter<>,
 valid?: false>

If you want any non-integer value to be converted to 0, you can do:

defp fix_params(%{count: count} = params) when not is_integer(count), do: %{params | count: 0}
defp fix_params(params), do: params

Demo:

iex(1)> Counter.changeset(%Counter{}, %{count: "foo"})
#Ecto.Changeset<action: nil, changes: %{count: 0}, errors: [],
 data: #MyApp.Counter<>, valid?: true>

Upvotes: 4

Related Questions