在Ecto中修改外键

问题描述 投票:32回答:5

我具有已经在上游运行并发送给上游的原始迁移:

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

我如何制定将根据我的要求更改此外键的迁移脚本?


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尝试重新创建约束之前就删除约束。

elixir phoenix-framework ecto
5个回答
19
投票

您可以在调用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])

34
投票

我不确定何时将其添加到Ecto,但至少在2.1.6中不再需要原始SQL。 drop/1现在supports constraints(虽然没有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

8
投票

我得到了以下解决方案:

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尝试重新创建约束之前就删除约束

从问题复制。


2
投票

我认为alter table无法实现。例如,根据this answer,Postgres不允许修改ALTER TABLE语句中的约束。 MySQL also doesn't allow modifying constraints

最简单的方法是删除该字段,如果没有任何数据,则将其重新添加。否则,您需要在execute

中使用原始SQL

0
投票

execute中:

“如果Ecto SQL 3.4.3值是:from,则适配器将在修改类型之前尝试删除相应的外键约束。”

%Reference{}

应该工作。在进行回滚时,我发现这可以清除FK并删除列:

modify :user_id, references(:users, on_delete: :delete_all), from: references(:users)
© www.soinside.com 2019 - 2024. All rights reserved.