首页 > 解决方案 > 在 Ecto 迁移中从另一个 Repo 迁移数据

问题描述

与这个问题类似:Using a Repo in an 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

问题是在运行 mix ecto.migrate 时我得到了

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

我尝试将其添加AnotherApp为 in 的依赖项MyAppmix.exs但仍然出现相同的错误。

这可以做到吗?

标签: elixirdatabase-migrationecto

解决方案


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

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

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

但是,有时确保它们已经启动是不够的:有时您必须明确启动该过程。在您的情况下,您必须启动其他应用程序的Ecto.Repo. 通常,您通过在应用程序的主管中列出Ecto 存储库来启动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

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

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

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

希望有帮助。


推荐阅读