Das Migrations-Cheat-Sheet enthält die wichtigsten Ecto-Migrationen - von einfachen Änderungen bis zu aufwändigeren Daten-Transformationen.

1. Tabellen-Migrationen

1.1. Tabelle erstellen

create table(:workshops_trainers) do
  add :user_id, references(:users)
  add :workshop_id, references(:workshops)
end

1.2. Tabelle umbenennen

rename table(:project), to: table(:projects)

1.3. Tabelle löschen

drop table(:trainers_workshops)

2. Spalten-Migrationen

2.1. Spalte hinzufügen

alter table(:workshops) do
  add :teaser_markdown, :text
  add :project_id, references(:projects), null: false, default: 1
end

2.2. Spalte umbenennen

rename table(:posts), :title, to: :summary

mariadb/mysql:

execute "ALTER TABLE events CHANGE COLUMN visible boolean;"

2.3. Spalte löschen

alter table(:workshops) do
  remove :agenda_html
end

2.4. Default-Wert einer Spalte verändern

alter table(:users) do
  modify :phone, :text, null: false
end

2.5. Default-Wert einer Spalte löschen

execute "ALTER TABLE xyz ALTER COLUMN project_id DROP DEFAULT;"

3. Indizes

3.1. Index erstellen

def change do
  create index(:user_identities, [:user_id])
end

3.2. Multi-Index setzen

create index(:user_identities, [:provider, :uid])

Wenn man einen Index für mehrere Spalten erstellt, kann man diese nicht über eine Liste bei unique_constraint angeben. Zur Lösung musst du den Namen des erzeugten Indizes angeben. Dieser setzt sich zusammen aus [tablle]_[index1]_[index2]_index. Die Ausgabe des Namens erfolgt in der Konsole.

Im Changeset:

|> unique_constraint(:uid, name: :user_identities_provider_uid_index)

4. Datenaktualisierung

Du solltest in den meisten Fällen Changesets aus deinem Schema vermeiden. Diese können sich im Laufe der Zeit verändern. Die Wahrscheinlichkeit ist hoch, dass deine Migrationen dann nicht mehr funktionieren. Arbeite alternativ mit Ecto.Changeset.cast und Ecto.Changeset.change.

4.1. Erstellen neuer Datensätze (Elixir)

use Ecto.Migration

alias MyProject.{Repo, Post}

def change do
  %Post{}
  |> Ecto.Changeset.change(title: "Hello!", content: "My name is...")
  |> Repo.insert!
end

4.2. Aktualisierungen von Datensätzen (Elixir)

alias MyProject.{Repo, Post}
use Ecto.Migration

def change do
  posts =
    Post
    |> Repo.all

  Enum.each(posts, &update_post(&1))
end

defp update_post(post) do
  updated_content =
    post.content
    |> String.replace(".com/system/", ".com/system/projects/1/")

  post
  |> Ecto.Changeset.change(content: updated_content)
  |> Repo.update!
end

4.3. Aktualisierungen von Datensätzen (SQL-like)

alias MyProject.{Repo, Event}
require Ecto.Query

def change do
  events =
    Event
    |> Repo.all()

  Enum.each(events, &update_event(&1))
end

defp update_event(event) do
  event_bookings = Ecto.Query.from p in "event_bookings", where: p.event_id == ^event.id

  event_bookings
  |> Repo.update_all(set: [price: event.booking_price])
end

5. Zusammenfassen von Migrationen

Wenn du Migrationen erstellst, die Daten verändern, wirst du irgendwann in Probleme laufen. Zum Beispiel, wenn du ein Modul umbenennst, dass in einer Migration genutzt wird. Irgendwann werden etliche Migrationen auch einfach unübersichtlich.

Wir gehst du dann vor?

  1. Du holst dir den letzten Stand deines Repositories.
  2. Du stellst sicher, dass dieser Stand auch schon deployed wurde (was die Migrationen angeht).
  3. Du sprichst dich mit deinem Team ab, ob alle die Migrationen ausgeführt haben.
  4. Du ruft mix ecto.dump auf.
  5. Du löscht die vorhandenen Migrationen aus priv/repo/migrations
  6. Du checkst die Datei priv/repo/structure.sql ein.