cjm2671
cjm2671

Reputation: 19486

How do I use Ecto.Enum with postgres ENUM types?

According to the documentation: https://hexdocs.pm/ecto/Ecto.Enum.html

It appears I should structure my migration as:

add :status, :string

The problem is, this doesn't take advantage of the Postgres ENUM type https://www.postgresql.org/docs/current/datatype-enum.html#id-1.5.7.15.5

The main purpose of having an ENUM is to reduce the field's storage footprint.

How should I structure the migration to take advantage of Postgres's built in ENUM, or should I use a different method?

Upvotes: 4

Views: 1323

Answers (2)

daemonThread
daemonThread

Reputation: 1044

From ecto 3.5 onwards we can use create_type() to create migration for enum type. Example: say we have a enum field user_role in user we can write the schema and migration in the following way

# lib/my_app/accounts/user_role.ex

defmodule MyApp.Accounts.UserRole do
  use EctoEnum, type: :user_role, enums: [:admin, :moderator, :seller, 
  :buyer]
end

# lib/my_app/accounts/user.ex


defmodule MyApp.Accounts.User do
 use Ecto.Schema
alias MyApp.Accounts.UserRole

 schema "users" do
  field :role, UserRole
 end
end

Migration for above schema

#priv/repo/migrations/20210102193646_add_role_to_users.exs

defmodule MyApp.Repo.Migrations.AddRoleToUsers do
use Ecto.Migration
alias MyApp.Accounts.UserRole

def change do
  UserRole.create_type()

  alter table(:users) do
    add :role, :user_role
  end
end
end

Upvotes: 0

sabiwara
sabiwara

Reputation: 3194

As it is mentioned in the docs, Ecto.Enum can actually be used with ENUM types as well:

Some databases also support enum types, which you could use in combination with the above.

AFAIK, there is no migration function for it though, you need to write it in SQL:

execute("CREATE TYPE my_status AS ENUM ('todo', 'wip', 'done');")

create_table :tasks do
  ...
  add :status, :my_status, null: false
  ...
end

The main purpose of having an ENUM is to reduce the field's storage footprint.

Ecto.Enum has many advantages even if used with a string field:

  • validates your schema, application side
  • handles conversion to atoms
  • using atoms is better than strings when working with known values, works well with typespecs and dialyzer

Besides, the use of an ENUM type on the database side has drawbacks as well, which I suppose is why it isn't pushed more in the docs. Updating the enum type to add/modify/fields can be tricky and comes with risks, as explained in the SQL Antipatterns book which recommends against it if the values might change later.

There is a third option in order to reduce the storage footprint, which is to store it as an integer. Quoting the docs:

# in migration:
add :status, :integer

# in schema:
field :status, Ecto.Enum, values: [foo: 1, bar: 2, baz: 5]

Upvotes: 10

Related Questions