Skip to content

Instantly share code, notes, and snippets.

@wosephjeber
Last active December 17, 2024 07:26
Show Gist options
  • Save wosephjeber/42472d6522d03161d710d5adb3dc3534 to your computer and use it in GitHub Desktop.
Save wosephjeber/42472d6522d03161d710d5adb3dc3534 to your computer and use it in GitHub Desktop.
Ecto migration for renaming table with indexes and constraints

Renaming table in Ecto migration

I recently wanted to rename a model and its postgres table in a Phoenix app. Renaming the table was simple and documented, but the table also had constraints, sequences, and indexes that needed to be updated in order for the Ecto model to be able to rely on default naming conventions. I couldn't find any examples of what this would look like but was eventually able to figure it out. For anyone else in the same situation, hopefully this example helps.

In the example below, I'm renaming the Permission model to Membership. This model belongs to a User and an Account, so it has foreign key constraints that need to be renamed.

defmodule MyApp.Repo.Migrations.RenamePermissionsToMemberships do
  use Ecto.Migration

  def up do
    # Drop the existing forgeign key and primary key constraints by their
    # default names from when they were generated automatically by Ecto.
    drop constraint(:permissions, "permissions_user_id_fkey")
    drop constraint(:permissions, "permissions_account_id_fkey")
    drop constraint(:permissions, "permissions_pkey")

    # Rename the table
    rename table(:permissions), to: table(:memberships)

    alter table(:memberships) do
      # "Modifying" the columns rengenerates the constraints with the correct
      # new names. These were the same types and options the columns were
      # originally created with in previous migrations.
      modify :id, :bigint, primary_key: true
      modify :user_id, references(:users)
      modify :account_id, references(:accounts)
    end

    # Rename the ID sequence. I don't think this affects Ecto, but it keeps
    # the naming and structure of the table more consistent.
    execute "ALTER SEQUENCE permissions_id_seq RENAME TO memberships_id_seq;"
  end
  
  # This is just the inverse, restoring the table and its constraints,
  # indexes, and sequences to their original names.
  def down do
    drop constraint(:memberships, "memberships_user_id_fkey")
    drop constraint(:memberships, "memberships_account_id_fkey")
    drop constraint(:memberships, "memberships_pkey")

    rename table(:memberships), to: table(:permissions)

    alter table(:permissions) do
      modify :id, :bigint, primary_key: true
      modify :user_id, references(:users)
      modify :account_id, references(:accounts)
    end

    execute "ALTER SEQUENCE memberships_id_seq RENAME TO permissions_id_seq;"
  end
end
@rcmonsayac
Copy link

@fuelen Very useful helper! Were you able to create unit tests for this? Tried imitating https://github.com/elixir-ecto/ecto_sql/blob/5f144a9cca515c1d4e26f05147fc0a2505c8989d/test/ecto/migration_test.exs#L4 but struggling to implement tests for this.
any tips?

@fuelen
Copy link

fuelen commented Mar 27, 2024

No, I don't test such migrations with unit tests. I simply check the result of migration using DB management tool

@johnhidey
Copy link

Just what I was looking for. Thanks 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment