在 Ecto migration 中从另一个 Repo 迁移数据

Migrate data from another Repo in Ecto migration

类似于这个问题:,我有一个 Ecto 迁移,我想在其中创建一些新表,但也从 不同的 迁移一些数据存储库。例如:

defmodule MyApp.Repo.Migrations.CreateFoo do
  use Ecto.Migration
  import Ecto.Query

  def change do
    create table(:foo) do
      add(:status, :text)
    end

    flush()
    execute &import_from_AnotherApp/0
  end

  defp import_from_AnotherApp()
    foos = from(f in AnotherApp.Foo, where: ...)
           |> AnotherApp.Repo.all

    Enum.each(foos, fn(foo) ->
      # Insert data from AnotherApp into MyApp foo table
    end)

  end
end

问题是当 运行 混合 ecto.migrate 我得到

** (RuntimeError) could not lookup Ecto repo AnotherApp.Repo because it was not started or it does not exist

我尝试在 mix.exs 中添加 AnotherApp 作为 MyApp 的依赖项,但我仍然遇到相同的错误。

这可以做到吗?

当您 运行 混合任务时,它们 运行 在自己的进程中,因此其他应用程序(包括容纳任务的主应用程序)可能无法启动。

有时您必须将这些行放入您的自定义混合任务中,我怀疑您可以将它们放入您的迁移中:

{:ok, _} = Application.ensure_all_started(:my_app)
{:ok, _} = Application.ensure_all_started(:another_app)

但是,有时仅确保它们已启动是不够的:有时您必须明确启动进程。在您的情况下,您必须启动其他应用程序的 Ecto.Repo。通常,您 start your Ecto repos 将它们列在 application.ex 内的应用程序主管中,例如

def start(_type, _args) do
  children = [
    {MyApp.Repo, []},
    {AnotherApp.Repo, []},
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

但是如果您的应用程序的常规功能不需要启动其他应用程序,那么您可以通过 运行ning MyApp.Repo.start_link([]) 手动启动该过程。 -- 你可以把它放到你的迁移中看看它是否出现:

x = MyApp.Repo.start_link([])
IO.inspect(x)

运气好的话,你会得到一个 :ok 和一个进程 ID,但如果没有,你应该得到一些有用的调试信息。

希望对您有所帮助。

也许 with_repo(repo, fun, opts \ []) 应该为你做 https://hexdocs.pm/ecto_sql/Ecto.Migrator.html#with_repo/3