Ecto migration with data entry?

I’d like to add a field to a table and populate the field as well in the migration. Working with examples always helps. Let’s add username to users table.

def up do
  alter table(:users) do
    add :username, :string, null: false
  end
  create unique_index(:users, [:username])

  from(u in App.User, update: [set: [username: u.id]])
  |> App.Repo.update_all([]) 

end

def down do
  alter table(:users) do
    remove :username
  end
end

The migration above gaves me: ** (Postgrex.Error) ERROR (undefined_column): column "username" of relation "users" does not exist. Is there a callback or something similar to the up and down functions?

3 Likes

Seems like it might be possible to do this using default and fragment? I’m not quite sure how to use fragment/1 though.

I’m not sure how to do this but you won’t be able to add that column with null: false if you already have users in your table since they will all have a null value.

I would probably do it in two steps.

  1. Run a migration that adds the column.
  2. Run a separate script to populate it.
  3. Run a new migration to add the null constraint.

But there may be a better way :slight_smile:

2 Likes

You need to call flush between the migration commands and the update.
See https://hexdocs.pm/ecto/2.1.0-rc.0/Ecto.Migration.html#flush/0

3 Likes

It seems like flush fixed
** (Postgrex.Error) ERROR (undefined_column): column "username" of relation "users" does not exist.

But as @Linuus mentioned, there are already users in the table so I’m now getting
** (Postgrex.Error) ERROR (not_null_violation): column "username" contains null values.

you can add the `default:` parameter to the migration. like this:

alter table(:ticket_types) do
  add :slug, :string, default: "", null: false
end
2 Likes