如何在自定义混合任务中从 Ecto 获取数据

Posted

技术标签:

【中文标题】如何在自定义混合任务中从 Ecto 获取数据【英文标题】:How to get data from Ecto in a custom mix task 【发布时间】:2016-11-08 14:06:18 【问题描述】:

我想在自定义混合任务中通过 Ecto 显示我的数据库中的数据。如何在我的任务中获取 Ecto 存储库(或启动它)?

我尝试了类似的方法,但没有成功:

defmodule Mix.Tasks.Users.List do


use Mix.Task
  use Mix.Config
  use Ecto.Repo, otp_app: :app

  @shortdoc "List active users"
  @moduledoc """
    List active users
  """
  def run(_) do
    import Ecto.Query, only: [from: 1]

    Mix.shell.info "=== Active users ==="
    query = from u in "users"
    sync = all(query)
    Enum.each(users, fn(s) -> IO.puts(u.name) end)
  end

end

当我启动 mix users.list 时,这会给我以下输出:

** (ArgumentError) repo Mix.Tasks.Users.List is not started, please ensure it is part of your supervision tree
    lib/ecto/query/planner.ex:64: Ecto.Query.Planner.query_lookup/5
    lib/ecto/query/planner.ex:48: Ecto.Query.Planner.query_with_cache/6
    lib/ecto/repo/queryable.ex:119: Ecto.Repo.Queryable.execute/5

有解决这个问题的想法或其他方法吗?

【问题讨论】:

【参考方案1】:

您需要确保在使用之前启动 repo

MyApp.Repo.start_link

【讨论】:

【参考方案2】:

Ecto 3.x:

ensure_started 已从 Ecto 中移除。围绕这个话题有很多困惑。请参阅此处https://github.com/elixir-ecto/ecto/pull/2829#issuecomment-456313417 了解更多信息。 José 建议使用Mix.Task.run "app.start" 启动应用程序或使用MyApp.Repo.start_link(...) 运行repo。

Ecto 2.x:

这曾经在 2.x 中工作,但显然 Mix.Ecto 不被视为公共 API 的一部分。

实际上有一个帮助模块Mix.Ecto (https://github.com/elixir-ecto/ecto/blob/master/lib/mix/ecto.ex) 可以更轻松地编写使用ecto 的混合任务:

defmodule Mix.Tasks.Users.List do
  use Mix.Task
  import Mix.Ecto

  def run(args) do
    repos = parse_repo(args)

    Enum.each repos, fn repo ->
      Mix.shell.info "=== Active users ==="

      ensure_repo(repo, args)
      ensure_started(repo, [])
      users = repo.all(Ectotask.User)

      Enum.each(users, fn(s) -> IO.puts(s.name) end)
    end
  end
end

这个助手让你可以访问parse_repo/1ensure_repo/2ensure_started/1parse_repo 将使您的任务与其他 ecto mix 任务很好地配合,例如,它可以让您通过 -r 来指定不同的 repo。

➤ mix users.list
=== Active users ===
Adam
➤ mix users.list -r Ectotask.Repo22
=== Active users ===
** (Mix) could not load Ectotask.Repo22, error: :nofile. Please pass a repo with the -r option.

ensure_started 确保 repo 正在运行,这是您所缺少的。

如需指导和启发,您可以在https://github.com/elixir-ecto/ecto/tree/master/lib/mix/tasks查看其他 ecto mix 任务是如何实现的

【讨论】:

谢谢。这就是我所缺少的! ! 自 Ecto 2.0 以来,ensure_started 的元数已从 1 更改为 2。第二个参数是关键字列表。我们应该将行 ensure_started(repo) 更改为 ensure_started(repo, []) 警告:你不应该import Mix.Ecto,因为它是一个私有 API,现在在 Ecto 3.0 中被破坏:github.com/elixir-ecto/ecto/issues/1586#issuecomment-233301683 @JasonAxelson 感谢您指出这一点!我已经更新了解决 ecto 3.0 的答案【参考方案3】:

在与 Phoenix 合作时,我还找到了另一个解决方案。我在priv/repo 中创建了一个新文件:

defmodule Users.List do
  def run() do
    Mix.shell.info "=== Active users ==="

    users = App.Repo.all(App.User)
    Enum.each(users, fn(s) ->
      Mix.shell.info("#s.name")
    end)
  end
end
Users.List.run

然后我从我的项目根目录使用mix run priv/repo/users.list.exs 运行它。

【讨论】:

【参考方案4】:

除了Jason Harrelson的回答:还需要启动PostgrexEcto

[:postgrex, :ecto]
|> Enum.each(&Application.ensure_all_started/1)

MyApp.Repo.start_link

更新:

另一种方法是使用混合任务启动应用程序:

Mix.Task.run "app.start", []

【讨论】:

以上是关于如何在自定义混合任务中从 Ecto 获取数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在自定义 Zapier 应用程序中从 Xero 访问特定订单项值?

Ecto - 如何通过特定查询获取“has_many”

如何在自定义 UIView 中从 ViewController 传递数据

微信小程序如何将接口获取的数据传递给自定义组件

如何在 OpenCV 2 中从图像中获取通道数?

如何从混合任务中运行混合任务?