DEV Community

Sushant Bajracharya
Sushant Bajracharya

Posted on • Edited on

Using Enum in Ecto and Phoenix

NOTE:

The internet is filled with programming articles written by inexperienced programmers. Same is with this article so read it with a grain of salt.

=====================================================================

If you are reading this, then chances are pretty wild that you know what enum is and why you want it. So, I wont bother wasting your time explaining them. Let's get straight to the point.

defmodule Stipe.Repo.Migrations.AlterDailyUpdate do use Ecto.Migration def up do execute("CREATE TYPE daily_update_status AS ENUM('In Progress', 'In Testing', 'Done');") execute("ALTER TABLE daily_updates ALTER COLUMN status DROP DEFAULT;") execute(" ALTER TABLE daily_updates ALTER COLUMN status TYPE daily_update_status USING CASE status WHEN NULL then 'In Progress' WHEN 0 then 'In Progress' end :: daily_update_status; ") execute(" ALTER TABLE daily_updates ALTER COLUMN status SET DEFAULT 'In Progress'; ") end def down do execute("ALTER TABLE daily_updates ALTER COLUMN status DROP DEFAULT;") execute(" ALTER TABLE daily_updates ALTER COLUMN status TYPE INT USING CASE status WHEN NULL then 0 WHEN 'In Progress' then 0 WHEN 'In Testing' then 1 WHEN 'Done' then 2 end :: integer; ") execute(" ALTER TABLE daily_updates ALTER COLUMN status SET DEFAULT 0; ") execute("DROP TYPE IF EXISTS daily_update_status;") end end 

This piece of code is a migration which alters the structure of the table DailyUpdates. Previously, I had set status column with integer but now I wanted to use postgres enum.

I like to define up and down function whenever I am using execute because when we use execute, migrations are not reversible.

The first line in up function is creating an enum data type. Then it drops any default value that was set from the previous migration. The enum data type is then assigned to the status column. If the status column has data 0 or NULL then it will be mapped. Anything else than that and the migration will fail.

The down function is reverting the migration.

Now, that we are done with migrations, let's go to our schema.

defmodule Stipe.Standup.DailyUpdate do use Ecto.Schema import Ecto.Changeset alias Stipe.Accounts.User schema "daily_updates" do field :remarks, :string field :started_on, :date field :status, :string field :task_number, :string field :time_spent, :decimal belongs_to :user, User timestamps() end def statuses do ["In Progress": "In Progress", "In Testing": "In Testing", Done: "Done"] end @doc false def changeset(daily_update, attrs) do daily_update |> cast(attrs, [:task_number, :status, :time_spent, :started_on, :remarks]) |> cast_assoc(:user) |> assoc_constraint(:user) |> validate_required([:task_number, :time_spent, :started_on, :remarks]) end end 

Normally we define our status field as type string. I have also defined statuses function that returns a list of our supported statuses. We can use this function to populate the form.

To use the statuses function in template, we will alias the Stipe.Standup.DailyUpdate in the view.

defmodule StipeWeb.DailyUpdateView do use StipeWeb, :view alias Stipe.Standup.DailyUpdate import Stipe.Utils.Date end 

In our template, we can call DailyUpdate.statuses

<%= form_for @changeset, @action, fn f -> %> <%= if @changeset.action do %> <div class="alert alert-danger"> <p>Oops, something went wrong! Please check the errors below.</p> </div> <% end %> <%= label f, :task_number %> <%= text_input f, :task_number %> <%= error_tag f, :task_number %> <%= label f, :status %> <%= select f, :status, DailyUpdate.statuses %> <%= error_tag f, :status %> <%= label f, :time_spent %> <%= number_input f, :time_spent, step: "any" %> <%= error_tag f, :time_spent %> <%= label f, :started_on %> <%= date_input f, :started_on %> <%= error_tag f, :started_on %> <%= label f, :remarks %> <%= text_input f, :remarks %> <%= error_tag f, :remarks %> <div> <%= submit "Save" %> </div> <% end %> 

It will list the supported statuses and also select the chosen status on edit.

Top comments (0)