If you find yourself converting atoms into strings before creating an Ecto changeset, you can careate a custom type and offload it to Ecto.
Wrong
updated = %{status: :open}
old_schema
|> Model.changeset(%{
status: updated.status |> Atom.to_string()
})
|> Repo.update()
Better
Instead, create a custom ecto type in utils/ecto/atom_type.ex
:
defmodule Project.Utils.Ecto.AtomType do
@moduledoc """
Cast atom type to string when dumping to database; cast to atom when reading from database.
"""
use Ecto.Type
def type, do: :string
def cast(value), do: {:ok, value}
def load(value), do: {:ok, String.to_atom(value)}
def dump(value) when is_atom(value), do: {:ok, Atom.to_string(value)}
def dump(value) when is_binary(value), do: {:ok, value}
def dump(_), do: :error
end
Then, make sure you specfy to use the custom type in your schema:
defmodule Project.Model do
use Ecto.Schema
import Ecto.Changeset
@type t :: %__MODULE__{}
@primary_key {:id, Ecto.UUID, autogenerate: false}
schema "models" do
field(:name, :string)
field(:status, Project.Utils.Ecto.AtomType)
timestamps()
end
Now, you can set the status value to atom or string, and it will be automagically converted to String when saving to DB. When you read it back, it will set it to atom type.
updated = %{status: :open}
old_schema
|> Model.changeset(updated)
|> Repo.update()
Thanks to Allan for suggesting a solution.
Happy Elixir Coding!