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?

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:

You need to call flush between the migration commands and the update.
See Ecto.Migration – Ecto v2.1.0-rc.0

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