在我的模式模型上设计ecto查询以支持预加载

时间:2018-12-18 17:43:17

标签: elixir phoenix-framework ecto

因此,我正在尝试为所有具有子关联的ecto模型提供模式。

在我的控制器中,有时我只想加载模型,而有时我想用全部或某些关联来加载它。

def get_account!(id), do: Repo.get!(Account, id)

帐户具有以下关联:

has_many :users
belongs_to :company

什么是修改我的get_account的好方法!功能让我可以选择是否预加载关联?

我不想在控制器中使用此预加载代码,我喜欢将所有与查询相关的内容都放在模块本身中,而不希望在控制器中泄漏。

2 个答案:

答案 0 :(得分:0)

一种方法是在上下文模块中创建with_<association>函数,例如:

def get_account!(id), do: Repo.get!(Account, id)

def with_company(%Account{} = account), do: Repo.preload(account, :company)

def with_users(%Account{} = account), do: Repo.preload(account, :users)

因此您可以像这样在控制器中使用它们:

def show(conn, %{"id" => id}) do
  account = 
    Context.get_account!(id)
    |> Context.with_company
    |> Context.with_users
  render(conn, "show.html", account: account)
end

,您在视图中有一个完全可用的帐户。

这种方法的好处是,您可以轻松地通过更具体的预加载来扩展它,例如account |> with_most_active_users(10)

答案 1 :(得分:0)

我通常从Rails范围的完成方法中借鉴方法。 Ecto查询接受另一个查询作为源。当所有预加载和其他条件都附加到查询之后,应该调用Repo.get系列函数,实际上是对数据库执行查询,这是最后一次调用。

因此,您可能会引入像这样的裸模板查询:

defp do_get_account_query(id),
  do: from(a in Account, where: a.id == ^id)

并在所有Ecto.Repo.one!/2查询中使用它:

def get_account!(id, preloads \\ []) do
  preloads
  Enum.reduce(do_get_account_query(id), fn clause, query ->
    Repo.preload(query, clause)
  end)
  |> Repo.one!()
end

对于更复杂的作用域,可以依赖于泛型函数并从中派生作用域。

相关问题