我有Meetup
模型has_many :rsvps
和Rsvp
模型belongs_to :user
。
这是我的终点:
def index(conn, _params) do
one_hour_ago = Timex.now
|> Timex.shift(hours: -1)
|> Timex.to_unix
meetups = from(m in Meetup, where: m.timestamp >= ^one_hour_ago)
|> Repo.all
|> Repo.preload(:rsvps)
render conn, meetups: meetups
end
和我的观点
def render("index.json", %{ meetups: meetups }) do
render_many(meetups, ParrotApi.MeetupView, "meetup.json")
end
def render("meetup.json", %{ meetup: meetup }) do
%{
id: meetup.id,
rsvped_users: Enum.map(meetup.rsvps, fn rsvp ->
rsvp |> Repo.preload(:user)
user = rsvp.user
%{
image_url: user.image_url,
interests: user.interests,
}
end),
}
end
然而,** (Plug.Conn.WrapperError) ** (KeyError) key :image_url not found in: #Ecto.Association.NotLoaded<association :user is not loaded>
将Repo
导入视图感觉不对。有没有办法在控制器中执行此操作?
答案 0 :(得分:1)
为此,Ecto支持嵌套预加载。你只需要改变:
|> Repo.preload(:rsvps)
为:
|> Repo.preload(rsvps: [:user])
在控制器中,所有Meetup的所有RSVP都会有效地预加载user
字段。
你几乎从不在循环中调用preload(for
/ Enum.each
/ Enum.map
等),因为它违背了预加载的主要目的 - avoiding N+1 queries。< / p>