Kooooro
Kooooro

Reputation: 453

Unable to preload multiple-hierarchy model

I have a 3-level hierarchy model relationship, that is, model1 belongs to model2 and model2 belongs to model3.

I'm trying to preload model2 and model3 for the model1:

  my_models = Repo.all(Model1)
    |> Enum.map(fn (x) ->
      Repo.preload(x, :model2)
    end) 
    |> Enum.map(fn(x) -> # error "no function clause matching in Ecto.Repo.Preloader.preload/4"
      Repo.preload(x.model2, :model3)
    end)

The error is "no function clause matching in Ecto.Repo.Preloader.preload/4"

Upvotes: 1

Views: 286

Answers (3)

Vadim Sergeevich
Vadim Sergeevich

Reputation: 153

Repo.all(from m in Model1, preload: [model2: :model3])

Good article - nested preload in ecto

Upvotes: 0

Oliver
Oliver

Reputation: 4081

Repo.preload([{:model2, :model3}])

Should do the trick. You can nest this structure however deep you want. E.g. 5 models deep:

Repo.preload([
  {:model1, [
    {:model2, [
      {:model3, [
        {:model4, :model5}
      ]}
    ]}
  ]}
])

Upvotes: 0

Gazler
Gazler

Reputation: 84140

You can do:

my_models = Repo.all(Model1) |> Repo.preload(model2: :model3)

Notice that this is not the same as:

my_models = Repo.all(Model1) |> Repo.preload([:model2, :model3])

As explained in https://hexdocs.pm/ecto/Ecto.Query.html#preload/3

If you can provide more of the error message, I'll be able to explain why you are getting that error message. It is likely that model2 (in the 2nd Enum.map) is nil, and https://github.com/elixir-ecto/ecto/blob/66e90c97054cd855c7cbb694bc79a0b9313f119b/lib/ecto/repo/preloader.ex#L36 only works with a map or a list.

Using the nested preload as above will handle this case for you.

Upvotes: 0

Related Questions