【问题标题】:designing ecto queries on my schema models to support preloading在我的模式模型上设计 ecto 查询以支持预加载
【发布时间】:2018-12-18 17:43:16
【问题描述】:

所以我正在尝试为我的所有具有子关联的 ecto 模型建立一个模式。

在我的控制器中,有时我只想加载模型,而其他时候我想加载所有或部分关联。

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

帐户具有以下关联:

has_many :users
belongs_to :company

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

我不想在我的控制器中包含此预加载代码,我喜欢在模块本身中包含与查询相关的所有内容,而不是在我的控制器中泄漏。

【问题讨论】:

    标签: elixir phoenix-framework ecto


    【解决方案1】:

    一种方法是在上下文模块中创建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 次 main + 调用 Repo.preload 的次数),因为 Repo.get!/2 返回的是一个对象,而不是一个查询。
    【解决方案2】:

    我通常借用 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
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-24
      相关资源
      最近更新 更多