Elixir/Ecto migrate and rollback

Defining my own database schema for a simple Phoenix/Elixir project to present a company dashboard. I started with the following objects that I wanted to store in the relational database :

  • Accounts
  • Users
  • Roles

A user belongs to an account and a user can have one or more roles. I created the following migration files (there was a single file per table) to accomplish this :

    # Accounts table
    defmodule Dashboard.Repo.Migrations.CreateAccount do
      use Ecto.Migration

      def change do
        create table(:accounts) do
          add :name, :string

          timestamps
        end
      end
    end

    # Users table
    defmodule Dashboard.Repo.Migrations.CreateUser do
      use Ecto.Migration

      def change do
        create table(:users) do
          add :name, :string
          add :username, :string, null: false
          add :email_address, :string
          add :password_hash, :string

          add :account_id, references(:accounts, on_delete: :nothing)
          timestamps
        end

        create unique_index(:users, [:username])
      end
    end

    # Roles table
    defmodule Dashboard.Repo.Migrations.CreateRole do
      use Ecto.Migration

      def change do
        create table(:roles) do
          add :name, :string

          timestamps
        end
      end
    end

    # User_roles (the association from a user to possibly more than one role)
    defmodule Dashboard.Repo.Migrations.CreateUserRole do
      use Ecto.Migration

      def change do
        create table(:user_roles) do
          add :user_id, references(:users)
          add :role_id, references(:roles)

          timestamps
        end
      end
    end

Since I’m just starting out I sort of stumbled to this set of migrations so I had to undo this more than once. Ecto and mix make this fairly painless during development. I can apply the migrations with :

    ▶ mix ecto.migrate

    16:21:05.920 [info]  == Running Dashboard.Repo.Migrations.CreateAccount.change/0 forward

    16:21:05.920 [info]  create table accounts

    16:21:05.951 [info]  == Migrated in 0.0s

    16:21:05.998 [info]  == Running Dashboard.Repo.Migrations.CreateUser.change/0 forward

    16:21:05.998 [info]  create table users

    16:21:06.002 [info]  create index users_username_index

    16:21:06.003 [info]  == Migrated in 0.0s

    16:21:06.020 [info]  == Running Dashboard.Repo.Migrations.CreateRole.change/0 forward

    16:21:06.020 [info]  create table roles

    16:21:06.022 [info]  == Migrated in 0.0s

    16:21:06.035 [info]  == Running Dashboard.Repo.Migrations.CreateUserRole.change/0 forward

    16:21:06.035 [info]  create table user_roles

    16:21:06.038 [info]  == Migrated in 0.0s

And then checking Postgres using psql I see :

    ▶ psql dashboard_dev
    psql (9.5.3)
    Type "help" for help.

    dashboard_dev=# \dt public.*
                   List of relations
     Schema |       Name        | Type  |  Owner
    --------+-------------------+-------+----------
     public | accounts          | table | postgres
     public | roles             | table | postgres
     public | schema_migrations | table | postgres
     public | user_roles        | table | postgres
     public | users             | table | postgres
    (5 rows)

The schema_migrations table contains all the migrations that have been applied by the ecto.migrate task to the database.

    dashboard_dev=# select * from schema_migrations;
        version     |     inserted_at
    ----------------+---------------------
     20160904190036 | 2016-09-04 20:21:05
     20160904190150 | 2016-09-04 20:21:06
     20160904190622 | 2016-09-04 20:21:06
     20160904194651 | 2016-09-04 20:21:06
    (4 rows)

The version is a generated number based on the current timestamp. To rollback you can do :

    ▶ mix ecto.rollback

    16:24:00.111 [info]  == Running Dashboard.Repo.Migrations.CreateUserRole.change/0 backward

    16:24:00.111 [info]  drop table user_roles

    16:24:00.115 [info]  == Migrated in 0.0s

And Ecto will rollback the last migration. To rollback everything applied to the database you can do :

    ▶ mix ecto.rollback --all

    16:24:39.191 [info]  == Running Dashboard.Repo.Migrations.CreateRole.change/0 backward

    16:24:39.191 [info]  drop table roles

    16:24:39.194 [info]  == Migrated in 0.0s

    16:24:39.223 [info]  == Running Dashboard.Repo.Migrations.CreateUser.change/0 backward

    16:24:39.223 [info]  drop index users_username_index

    16:24:39.224 [info]  drop table users

    16:24:39.225 [info]  == Migrated in 0.0s

    16:24:39.241 [info]  == Running Dashboard.Repo.Migrations.CreateAccount.change/0 backward

    16:24:39.241 [info]  drop table accounts

    16:24:39.242 [info]  == Migrated in 0.0s

You can now see that all the tables are gone with :

    ▶ psql dashboard_dev
    psql (9.5.3)
    Type "help" for help.

    dashboard_dev=# \dt public.*
                   List of relations
     Schema |       Name        | Type  |  Owner
    --------+-------------------+-------+----------
     public | schema_migrations | table | postgres
    (1 row)

    dashboard_dev=# select count(*) from schema_migrations ;
     count
    -------
         0
    (1 row)

Other use cases are covered in the documentation for Ecto.Migrate and Ecto.Rollback.




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Elixir, Phoenix Framework and Datatables
  • Elixir/OTP Supervision
  • Elxiir Unit Tests and Iterating a Single Test
  • Elixir/Ecto - update_all with join
  • Elixir and Ecto's telemetry events