У меня есть столбец :from
, который изначально был типа {:array, :string}
. Теперь я хочу перенести этот столбец типа :string
, взяв первую запись массива в качестве нового значения.
В Rails вы можете сделать это с помощью некоторой пользовательской логики в процессе миграции. Я пытаюсь сделать то же самое с Ecto, но у меня возникли проблемы из-за проверки схемы и ошибок набора изменений.
defmodule Assistant.Repo.Migrations.ChangeFromFieldOnMails do
use Ecto.Migration
def up do
dict_of_froms =
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, List.first(mail.from))
end)
alter table(:mails) do
remove :from
add :from, :string
end
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.each(fn mail ->
changeset = Ecto.Changeset.change(mail, from: Map.get(dict_of_froms, mail.id))
Assistant.Repo.update!(changeset)
end)
end
def down do
dict_of_froms =
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.reduce(%{}, fn mail, acc ->
Map.put(acc, mail.id, [mail.from])
end)
alter table(:mails) do
remove :from
add :from, {:array, :string}
end
Assistant.Mail
|> Assistant.Repo.all()
|> Enum.each(fn mail ->
changeset = Ecto.Changeset.change(mail, from: Map.get(dict_of_froms, mail.id))
Assistant.Repo.update!(changeset)
end)
end
end
Проблема в том, что мне также придется изменить field :from, {:array, :string}
на field :from, :string
в моей Mail
схеме, и это вызывает проблемы с проверкой.
На шаге up
Assistant.Repo.all()
завершится ошибкой, поскольку Ecto не может загрузить поле from
из старой БД из-за несоответствия типов.
На шаге down
Assistant.Repo.update!(changeset)
завершится ошибкой, поскольку Ecto.Changeset
сообщил об ошибке несоответствия типов на :from
.
В Rails на самом деле нет строгой проверки схемы, поэтому вы можете сойти с кода.
Как правильно выполнять такие миграции с помощью Ecto? Нет ли другого способа, кроме написания собственного SQL?