Подтвердить что ты не робот

Изменение внешнего ключа в 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])

Теперь я хочу изменить внешний ключ на user_id на каскадные удаления, чтобы при удалении пользователя все связанные с ним видео также были удалены.

Я пробовал следующую миграцию:

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

Но это вызывает ошибку:

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

Как я могу сформулировать миграцию script, которая изменит этот внешний ключ в соответствии с моим требованием?


UPDATE

У меня получилось следующее решение:

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

это сбрасывает ограничение перед тем, как ecto пытается его воссоздать.

4b9b3361

Ответ 1

Вы можете отбросить индекс перед вызовом alter:

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

Противоположность немного сложнее:

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

Ответ 2

Я не уверен, когда это было добавлено в Ecto, но по крайней мере в 2.1.6 больше нет необходимости в сыром SQL. drop/1 теперь поддерживает ограничения (drop_if_exists/1 не поддерживает):

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

Ответ 3

У меня получилось следующее решение:

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

это сбрасывает ограничение перед тем, как ecto пытается его воссоздать.

Скопирован из вопроса.

Ответ 4

Я не думаю, что это может быть достигнуто с помощью alter table. Например, согласно этот ответ Postgres не позволяет изменять ограничения в инструкции alter table. MySQL также не позволяет изменять ограничения.

Самый простой способ - удалить поле и добавить его обратно, если у вас нет данных. В противном случае вам нужно использовать raw SQL с execute