How can I preload association and get it returned in ecto?
Asked Answered
R

3

9

Let say I have a User model which has_many Post.

I fetched a user:

user = Repo.get(User, 1)

and now I want to get all posts for this user. The only solution I found is to:

posts = Repo.preload(user, :posts).posts

But it's ugly. Is there any shorthand for that?

Resignation answered 6/10, 2016 at 13:0 Comment(0)
A
12

You can use Ecto.assoc/2 to get a query for all the posts and then pass that to Repo.all/1 to actually fetch them:

iex(1)> user = Repo.get(User, 1)
iex(2)> Ecto.assoc(user, :posts)
#Ecto.Query<from p in MyApp.Post, where: p.user_id == ^1>
iex(3)> Ecto.assoc(user, :posts) |> Repo.all
[debug] QUERY OK source="posts" db=2.4ms
SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."user_id" = $1) [1]
...
Allisan answered 6/10, 2016 at 13:32 Comment(4)
What's the difference between assoc and preload ?Ambary
assoc builds a query for particular association without retrieving data from db, preload retrieves data from db for association including nested associations.Minium
Note that preload/2 returns both the parent record (user in this case) and the associated records (posts) in one data structure, while assoc/2 piped to Repo.all only returns the associated records (posts). This means if you need the parent record, you can get them separately (as in the example above) but you won't have that data if you write it entirely as piped statements.Bellarmine
In case the association was already loaded, Repo.preload/2 won't attempt to reload it.Trichroism
T
15
user = Repo.get(User, 1) |> Repo.preload([:posts])
Trouveur answered 22/12, 2017 at 12:31 Comment(1)
While this code might answer the question, it is usually considered good practice to add an explanation of what your code does. This allows for developers who are not knowledgeable in this area to understand what is going on in the code and helps them to learn how to solve the problem by them selves in the future.Burnley
A
12

You can use Ecto.assoc/2 to get a query for all the posts and then pass that to Repo.all/1 to actually fetch them:

iex(1)> user = Repo.get(User, 1)
iex(2)> Ecto.assoc(user, :posts)
#Ecto.Query<from p in MyApp.Post, where: p.user_id == ^1>
iex(3)> Ecto.assoc(user, :posts) |> Repo.all
[debug] QUERY OK source="posts" db=2.4ms
SELECT p0."id", p0."title", p0."user_id", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."user_id" = $1) [1]
...
Allisan answered 6/10, 2016 at 13:32 Comment(4)
What's the difference between assoc and preload ?Ambary
assoc builds a query for particular association without retrieving data from db, preload retrieves data from db for association including nested associations.Minium
Note that preload/2 returns both the parent record (user in this case) and the associated records (posts) in one data structure, while assoc/2 piped to Repo.all only returns the associated records (posts). This means if you need the parent record, you can get them separately (as in the example above) but you won't have that data if you write it entirely as piped statements.Bellarmine
In case the association was already loaded, Repo.preload/2 won't attempt to reload it.Trichroism
L
4
user = Repo.get(User, 1) |> preload(:posts)

Is the shorthand for your case

Lenka answered 1/10, 2017 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.