19
votes

Is there any way to preload ecto associations without explicitly using preload:?

Something like an option in the schema?

schema "gadgets" do
  field :foo,
  has_many :bars, Myapp.Bar, preload: true
end

I'm doing something like

Repo.get(Gadget, id)
  |> Repo.preload: [:bars]

Edit: the reason I'm trying to do this is because I want to preload a related model to the already preloaded related model, like

 preload: [:invoices preload: :items] 
2

2 Answers

32
votes

You can also preload as part of a query:

defmodule Gadget do
  use Ecto.Model

  # ...

  def with_invoices(query) do
    from q in query, preload: [invoices: :items]
  end
end

Then:

Gadget
|> Gadget.with_invoices
|> Repo.get!(id)
-2
votes

I'm not sure it is the fastest way, but I ended up doing this using the after_load callback, like this:

defmodule Invoice do
  use Ecto.Model

  after_load :preload_items

  def preload_items(invoice) do
    invoice |> Repo.preload([:items])
  end
end

Now, every time Invoice is loaded, even if it is preloaded by something else, it will preload its associated items.

Edit - Don't do this

Put the preloads in the query instead. retrieving 1000 invoices with above code will result in 1 + 1000 queries. the preload in the query adds 0NE. query. 1 + 1 < 1000 + 1.

  query = from c in Gadget,
  #retrieve nested associations adds one query
  preload: [invoices: :items]
  select c

Repo.all(query)