Ajouter une migration Ecto dans AgeOnline. Utiliser pour: nouvelle colonne, nouvelle table, modifier le schéma, ajouter un index, supprimer un champ, changer un type. Produit: fichier migration numéroté, schéma Elixir mis à jour, seeds adaptés si besoin, test nominal + erreur.
NNNN_<nom_snake_case>.exs correctement numérotépriv/repo/seeds.exs si la migration affecte les données initialesIdentifier le numéro de migration le plus élevé dans backend/age_online_api/priv/repo/migrations/.
Le format est NNNN_nom.exs (ex: 0012_add_progression_to_users.exs).
Le prochain numéro est N + 1 avec zéro-padding à 4 chiffres (ex: 0013).
priv/repo/migrations/
├── 0001_create_users.exs
├── ...
└── 0012_add_progression_to_users.exs ← actuel max
| Besoin | Pattern à utiliser |
|---|---|
| Nouvelle table | create table(:nom) do … end + index |
| Nouvelle(s) colonne(s) | alter table(:nom) do add … end |
| Supprimer une colonne | alter table(:nom) do remove … end |
| Modifier un type | alter table(:nom) do modify … end |
| Nouvel index | create index(:nom, [:col]) ou unique_index |
| JSONB map | :map comme type Ecto |
| Array | {:array, :string} comme type Ecto |
defmodule AgeOnlineApi.Repo.Migrations.NomEnPascalCase do
use Ecto.Migration
def change do
alter table(:nom_table) do
# Description du champ
add :nom_colonne, :type, default: valeur, null: false
end
end
end
defmodule AgeOnlineApi.Repo.Migrations.CreateNomTable do
use Ecto.Migration
def change do
create table(:nom_table) do
add :champ1, :string, null: false
add :champ2, :integer, default: 0, null: false
add :owner_id, references(:users, on_delete: :delete_all)
timestamps()
end
create index(:nom_table, [:owner_id])
end
end
Le schéma correspondant se trouve dans backend/age_online_api/lib/age_online_api/.
| Table | Schéma |
|---|---|
users | user.ex |
castles | castle.ex |
units | unit.ex |
map_tiles | map_tile.ex |
Trois zones à mettre à jour dans le schéma :
schema "nom_table" do
# 1. Ajouter le champ ici
field :nouveau_champ, :type, default: valeur
end
def changeset(struct, attrs) do
struct
# 2. Ajouter le champ dans cast/2
|> cast(attrs, [..., :nouveau_champ])
# 3. Ajouter dans validate_required/2 si null: false sans default
|> validate_required([...])
end
null: false sans default→ Ajouter dans validate_required du changeset.
null: false avec default→ Ne PAS ajouter dans validate_required, la DB fournit la valeur.
Ouvrir backend/age_online_api/priv/repo/seeds.exs.
Adapter si la migration :
null: false sans default sur une table peuplée par les seedsMapSeeder ou autre module de seedLaisser intact si :
Le test s'ajoute dans backend/age_online_api/test/api_e2e_test.exs ou dans un nouveau fichier test/<module>_test.exs.
Pattern de test en place :
setup do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(AgeOnlineApi.Repo)
Ecto.Adapters.SQL.Sandbox.mode(AgeOnlineApi.Repo, {:shared, self()})
:ok
end
{:error, changeset}unique_index ajoutéChecklist avant de considérer la migration terminée :
use Ecto.Migration présentmix run priv/repo/seeds.exs mentalement)cd backend/age_online_api
# Appliquer la migration
mix ecto.migrate
# Annuler la dernière migration
mix ecto.rollback
# Vérifier l'état des migrations
mix ecto.migrations
# Lancer les tests
mix test
# Réinitialiser complètement (dev uniquement)
mix ecto.reset
| Type Elixir | Type Postgres |
|---|---|
:string | varchar |
:integer | integer |
:boolean | boolean |
:float | float |
:map | jsonb |
{:array, :string} | varchar[] |
:text | text |
:utc_datetime | timestamp |
references(:table) | foreign key |