2016-02-01 10 views
17

mam ten oryginalny migracji, które zostały już uruchomić i wysłany upstream:Modyfikuj klucz obcy w Ecto

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]) 

Teraz chciałbym zmienić klucz obcy na user_id kaskady delecji, tak, że gdy użytkownik zostanie usunięty wszystkie powiązane z nim filmy również zostaną usunięte.

Próbowałem następujące migracji:

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

Ale tu pojawia się błąd:

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

Jak mogę sformułować scenariusz migracji, które zmienią ten klucz obcy według moich wymagań?


UPDATE

skończyło się następujące rozwiązanie:

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 

ten spada ograniczenie przed ekto próbuje go odtworzyć.

+4

Masz odpowiedź pracuje w kwestionujesz, it'be przydatna, aby opublikować go jako oddzielny odpowiedź więc każdy może głosować. –

Odpowiedz

13

można upuścić indeks przed wywołaniem alter:

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

Robi odwrotnie jest trochę trudniejsze:

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

Skończyłem na zrobieniu wariacji tego. –

+0

To zadziałało dla mnie świetnie. – ghayes

+0

Linia 'drop_if_exists index (: videos, [: user_id])' nie ma sensu, ponieważ usuwa normalny indeks, a nie ograniczenie obcego klucza.Odpowiedź Jessie jest lepsza IMO: https://stackoverflow.com/a/43572806/175830 –

2

Nie sądzę, że można to osiągnąć dzięki alter table. Na przykład zgodnie z this answer Postgres nie zezwala na modyfikowanie ograniczeń w instrukcji ALTER TABLE. MySQL also doesn't allow modifying constraints.

Najprostszym rozwiązaniem jest usunięcie pola i dodanie go, jeśli nie masz żadnych danych. W przeciwnym razie trzeba użyć surowego SQL z execute

+1

Miałem nadzieję, że jest lepszy sposób na zrobienie tego. W tym konkretnym przykładzie mogłem po prostu upuścić kolumnę i ponownie ją dodać, ale gdyby była to sytuacja w świecie rzeczywistym, kolumna prawdopodobnie zawierałaby już cenne informacje, których byłoby mi bardzo przykro stracić :) –

4

skończyło się z następujących rozwiązanie:

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 

to powoduje spadek ograniczenie przed próbą odtworzenia ecto

Skopiowano z pytania.

+1

To powinno być wyższe –

+1

Nie należy używać 'flush()'? Ograniczenie upuszczania (wideo: "video_user_id_fkey") 'powinno działać. –

7

Nie jestem pewien, kiedy został dodany do Ecto, ale przynajmniej w wersji 2.1.6 nie ma już potrzeby stosowania surowego SQL. drop/1 teraz supports constraints:

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