web-dev-qa-db-fra.com

Modifier la clé étrangère dans Ecto

J'ai cette migration originale qui a déjà été exécutée et envoyée en amont:

create table(:videos) do
  add :url, :string
  add :title, :string
  add :description, :text
  add :user_id, references(:users, on_delete: :nothing)

  timestamps
end
create index(:videos, [:user_id])

Maintenant, je souhaite changer la clé étrangère sur user_id pour effectuer des suppressions en cascade, de sorte que lorsqu'un utilisateur est supprimé, toutes ses vidéos associées le soient également.

J'ai essayé la migration suivante:

alter table(:videos) do
  modify :user_id, references(:users, on_delete: :delete_all)
end

Mais cela soulève une erreur:

(Postgrex.Error) ERROR (duplicate_object): constraint "videos_user_id_fkey" for relation "videos" already exists

Comment puis-je formuler un script de migration qui modifiera cette clé étrangère en fonction de mes besoins?


MISE À JOUR

Je me suis retrouvé avec la solution suivante:

def up do
  execute "ALTER TABLE videos DROP CONSTRAINT videos_user_id_fkey"
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :delete_all)
  end
end

def down do
  execute "ALTER TABLE videos DROP CONSTRAINT videos_user_id_fkey"
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :nothing)
  end
end

cela supprime la contrainte avant qu'ecto n'essaye de la recréer.

32
Thomas Dippel

Vous pouvez supprimer l'index avant d'appeler alter:

drop_if_exists index(:videos, [:user_id])
alter table(:videos) do
  modify :user_id, references(:users, on_delete: :delete_all)
end

Faire l'inverse est un peu plus délicat:

execute "ALTER TABLE videos DROP CONSTRAINT videos_user_id_fkey"
create_if_not_exists index(:videos, [:user_id])
18
Gazler

Je ne sais pas quand cela a été ajouté à Ecto, mais au moins dans 2.1.6 il n'y a plus besoin de SQL brut. drop/1 maintenant prend en charge les contraintes (drop_if_exists/1 pas si):

def up do
  drop constraint(:videos, "videos_user_id_fkey")
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :delete_all)
  end
end

def down do
  drop constraint(:videos, "videos_user_id_fkey")
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :nothing)
  end
end
27
Max Clarke

Je me suis retrouvé avec la solution suivante:

def up do
  execute "ALTER TABLE videos DROP CONSTRAINT videos_user_id_fkey"
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :delete_all)
  end
end

def down do
  execute "ALTER TABLE videos DROP CONSTRAINT videos_user_id_fkey"
  alter table(:videos) do
    modify :user_id, references(:users, on_delete: :nothing)
  end
end

cela supprime la contrainte avant que ecto essaie de le recréer

Copié de la question.

8
Jesse Shieh

Je ne pense pas que ce soit possible avec alter table. Par exemple selon cette réponse Postgres ne permet pas de modifier les contraintes dans ALTER TABLE déclaration. MySQL ne permet pas non plus de modifier les contraintes .

La chose la plus simple à faire serait de supprimer le champ et de l'ajouter à nouveau si vous n'avez pas de données. Sinon, vous devez utiliser du SQL brut avec execute

2
tkowal