我具有已经在上游运行并发送给上游的原始迁移:
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尝试重新创建约束之前就删除约束。
您可以在调用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])
我不确定何时将其添加到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
我得到了以下解决方案:
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尝试重新创建约束之前就删除约束
从问题复制。
我认为alter table
无法实现。例如,根据this answer,Postgres不允许修改ALTER TABLE
语句中的约束。 MySQL also doesn't allow modifying constraints。
最简单的方法是删除该字段,如果没有任何数据,则将其重新添加。否则,您需要在execute
在execute
中:
“如果Ecto SQL 3.4.3值是:from
,则适配器将在修改类型之前尝试删除相应的外键约束。”
%Reference{}
应该工作。在进行回滚时,我发现这可以清除FK并删除列:
modify :user_id, references(:users, on_delete: :delete_all), from: references(:users)