Associations
Associations in Ecto are used when two difference sources(tables) are linked via foreign keys.
A classic example of this setup is “Post has many comments”. First create the two tables in migrations
1 | create table(:posts) do |
Each comment contains a post_id
column that by default points to a post id
And now defined the schemas
1 | defmodule MyApp.Post do |
Querying associations
One of the benefits of defining associations is taht they can be used in queries. For example:
1 | Repo.all from p in Post, preload: [:comments] |
Now all posts will be fetched from the database with their associated comments. The example above will perform two queries: one for loading all posts and another for loading all comments. This is often the most efficient way of loading associations from the database(even if two queries are performed) because we need to receive and parse only POSTS + COMMENTS results.
It is also possible to preload associations using joins while performing more complex queries. For example, imagine both posts and comments have votes and you want only comments with more votes than the post itself:
1 | Repo.all from p in Post, |
Manipulating associations
While Ecto 2.0 allows you insert a post with multiple comments in one operation:
1 | Repo.insert!(%Post{ |
Many times you may want to break it into distinct steps so you have more flexibility in managing those entries. For example, you could use changesets to build your posts and comments along the way
1 | post = Ecto.Changeset.change(%Post{}, title: "Hello", body: "World") |
Or by handling each entry individually inside a transation:
1 | Repo.transaction fn -> |
Ecto.build_assoc/3
builds the comment using the id currently set in the post struct. It is equivalnet to:
1 | %Comment{post_id: post.id, body: "Execellent"} |
The Ecto.build_assoc/3 function is specially useful in Phoenix controllers. For example when creating the post, one would do:
1 | Ecto.build_assoc(current_user, :post) |
As we likely want to associate the post to the user currently signed in the application. In another controller, we could build a comment for an existing post with:
1 | Ecto.build_assoc(post, :comments) |
Ecto does not provide functions like post.comments << comment
that allows mixing persisted data with non-persisted data. The only macha