修改Ecto中的外键

时间:2016-02-01 22:47:43

标签: elixir phoenix-framework 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

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

更新

我最终得到了以下解决方案:

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

6 个答案:

答案 0 :(得分:25)

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

答案 1 :(得分:18)

您可以在致电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 :(得分: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尝试重新创建它之前删除约束

从问题中复制。

答案 3 :(得分:2)

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

最简单的方法是删除字段并在没有任何数据时将其添加回去。否则,您需要使用带有execute

的原始SQL

答案 4 :(得分:2)

Ecto SQL 3.4.3中:

“如果:from的值为%Reference{},则适配器将在修改类型之前尝试删除相应的外键约束。”

modify :user_id, references(:users, on_delete: :delete_all), from: references(:users)

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

remove :user_id, references(:users)

答案 5 :(得分:0)

... import { Home, SomeOtherComponent } from './pages'; ... 选项是在 Ecto 3.0 中引入的,因此您可以以更优雅的方式设置迁移:

from