修改 Ecto 中的外键
Modify foreign key in 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 尝试重新创建约束之前删除约束。
我认为 alter table
无法实现。例如根据 this answer Postgres doesn't allow modifying constraints in ALTER TABLE
statement. MySQL also doesn't allow modifying constraints.
最简单的方法是删除该字段,如果您没有任何数据,再将其添加回去。否则,您需要使用 SQL 和 execute
您可以在调用之前删除索引 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])
我最终得到了以下解决方案:
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 尝试重新创建约束之前删除约束
从问题中复制。
我不确定这是什么时候添加到 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
"If the :from
value is a %Reference{}
, the adapter will try to drop the corresponding foreign key constraints before modifying the type."
modify :user_id, references(:users, on_delete: :delete_all), from: references(:users)
应该可以。在进行回滚时,我发现这可以清除 FK 并删除列:
remove :user_id, references(:users)
from
选项在 Ecto 3.0 中引入,因此您可以以更优雅的方式设置迁移:
defmodule App.Migrations.ChangeVideosUserConstraint do
use Ecto.Migration
def change do
alter table(:videos) do
modify(:user_id, references(:users, on_delete: :delete_all), from: references(:users))
end
end
end
我有这个原始迁移已经 运行 并发送到上游:
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 尝试重新创建约束之前删除约束。
我认为 alter table
无法实现。例如根据 this answer Postgres doesn't allow modifying constraints in ALTER TABLE
statement. MySQL also doesn't allow modifying constraints.
最简单的方法是删除该字段,如果您没有任何数据,再将其添加回去。否则,您需要使用 SQL 和 execute
您可以在调用之前删除索引 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])
我最终得到了以下解决方案:
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 尝试重新创建约束之前删除约束
从问题中复制。
我不确定这是什么时候添加到 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
"If the :from
value is a %Reference{}
, the adapter will try to drop the corresponding foreign key constraints before modifying the type."
modify :user_id, references(:users, on_delete: :delete_all), from: references(:users)
应该可以。在进行回滚时,我发现这可以清除 FK 并删除列:
remove :user_id, references(:users)
from
选项在 Ecto 3.0 中引入,因此您可以以更优雅的方式设置迁移:
defmodule App.Migrations.ChangeVideosUserConstraint do
use Ecto.Migration
def change do
alter table(:videos) do
modify(:user_id, references(:users, on_delete: :delete_all), from: references(:users))
end
end
end