(追記)本記事は2022/10/23 に更新しました
Ecto にはテーブル同士を関連付ける Ecto Association の機能があります。関連付けには以下の3つのタイプが考えられます。
- one-to-one
- one-to-many
- many-to-many
Ecto Association は以下の公式サイトで説明されています。今回はこのガイドを実際に実行し、補足説明を加えていきたいと思います。
Ecto Association Guide
0.Ectoの設定
まずは Ecto の設定を、実際にプロジェクトを作成しながら行っていきます。mix で ecto_assoc というプロジェクトを作成します。オプションの --sup はこのアプリケーションが supervision tree を持っていることを示しています。
mix new ecto_assoc --sup
プロジェクトディレクトリに移ります。
cd ecto_assoc
次に必要なパッケージをインストールします。以下のファイルのdepsを変更します。postgrex はPostgreSQLのドライバです。
defp deps do
[{:ecto, "~> 2.0"},
{:postgrex, "~> 0.11"}]
end
インストールします。
mix deps.get
まず以下のコマンドで **Repo (lib/ecto_assoc/repo.ex)**を作成します。
mix ecto.gen.repo -r EctoAssoc.Repo
repo.ex を確認します。EctoAssoc.Repo はデータベースへの query に使われます。ここではEcto.Repo を使うことが宣言されています。otp_app はどのアプリがデータベースへアクセスしているかを教えています。ここでは :ecto_assoc となっているのでこのプロジェクトの config.exs を参照し、その接続情報をもとにデータベースにアクセスします。
defmodule EctoAssoc.Repo do
use Ecto.Repo, otp_app: :ecto_assoc
end
さらに config/config.exs の DB 設定を確認します。上で指定した :ecto_assoc の設定です。必要であれば、username/password などを自分の環境に合わせます。最後の行は、アプリケーションに対して repo について教えます。
config :ecto_assoc, EctoAssoc.Repo,
adapter: Ecto.Adapters.Postgres,
database: "ecto_assoc_repo",
username: "user",
password: "pass",
hostname: "localhost"
config :ecto_assoc, ecto_repos: [EctoAssoc.Repo]
次に lib/ecto_assoc/application.ex を編集します。EctoAssoc.Repo を application の supervision tree の中の supervisor としてセットします。これによって、Ecto process を起動して、アプリのqueryを受け取り実行できるようになります。
#
import Supervisor.Spec
def start(_type, _args) do
children = [
supervisor(EctoAssoc.Repo, [])
]
#
最後に次のコマンドで DB を作成します。
mix ecto.create
1.One-to-one (User-Avatar)
まず One-to-one の Ecto Association を見ていきます。まず usersテーブルと avatarsテーブルを作成します。次に両テーブルに関連性を与えます。最後に実際の実行結果を確認したいと思います。
1-1.User
usersテーブルを作成する migrations を作ります。migrations とは Elixir の記述だけでデータベースのテーブル操作をしてしまおうとするものです。
mix ecto.gen.migration create_user
以下のような空の migrations が作成されます。
efmodule EctoAssoc.Repo.Migrations.CreateUser do
use Ecto.Migration
def change do
end
end
これを以下のように修正します。
defmodule EctoAssoc.Repo.Migrations.CreateUser do
use Ecto.Migration
def change do
create table(:users) do
add :name, :string
add :email, :string
end
end
end
User schemaを作成します
defmodule EctoAssoc.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
end
end
1-2.Avatar
avatars テーブルを作成する migrations を作ります。
mix ecto.gen.migration create_avatar
作成されたmigrationsを以下のように修正します。
defmodule EctoAssoc.Repo.Migrations.CreateAvatar do
use Ecto.Migration
def change do
create table(:avatars) do
add :nick_name, :string
add :pic_url, :string
end
end
end
Avatar schemaを作成します
defmodule EctoAssoc.Avatar do
use Ecto.Schema
schema "avatars" do
field :nick_name, :string
field :pic_url, :string
end
end
以上で準備が整いましたので、DBとテーブルを作成したいと思います。
mix ecto.create
mix ecto.migrate
1-3.Associationsを追加
avatars テーブルを変更する migration を作成します。
mix ecto.gen.migration avatar_belongs_to_user
どのように変更するかを記述します。ここでは user_id を users テーブルを参照する外部キーとして追加するように変更します。
defmodule EctoAssoc.Repo.Migrations.CreateAvatar do
use Ecto.Migration
def change do
alter table(:avatars) do
add :user_id, references(:users)
end
end
end
Avatar schema を修正(追加)します。ここでは belongs_to field を追加します。これにより avatar.user で users テーブルにアクセスできるようになります。
defmodule EctoAssoc.Avatar do
use Ecto.Schema
schema "avatars" do
field :nick_name, :string
field :pic_url, :string
belongs_to :user, EctoAssoc.User # 追加
end
end
User schema を修正(追加)します。これによる usersテーブルの変更はありませんが、user.avatar によって avatars テーブルへアクセスできるようになります。
defmodule EctoAssoc.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
has_one :avatar, EctoAssoc.Avatar # 追加
end
end
migrate を実行して、実際にテーブルを変更します。
mix ecto.migrate
1-4.実行結果の確認
iex -S mix
### Repo, User, Avatarモジュール名の短縮でアクセス可能にします。
iex(1)> alias EctoAssoc.{Repo, User, Avatar}
[EctoAssoc.Repo, EctoAssoc.User, EctoAssoc.Avatar]
### Userを、Avater無しで、作成します。
iex(2)> user = %User{name: "John Doe", email: "john.doe@example.com"}
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:built, "users">,
avatar: #Ecto.Association.NotLoaded<association :avatar is not loaded>,
email: "john.doe@example.com",
id: nil,
name: "John Doe"
}
### Userをテーブルに挿入します。insert!(ビックリマーク)は例外を発生します。
### この時点で初めてUserはテーブルで実体化され、idが決まります。
iex(3)> user = Repo.insert!(user)
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
avatar: #Ecto.Association.NotLoaded<association :avatar is not loaded>,
email: "john.doe@example.com",
id: 1,
name: "John Doe"
}
### Avaterを作成します。
iex(4)> avatar = %Avatar{nick_name: "Elixir", pic_url: "http://elixir-lang.org/images/logo.png"}
%EctoAssoc.Avatar{
__meta__: #Ecto.Schema.Metadata<:built, "avatars">,
id: nil,
nick_name: "Elixir",
pic_url: "http://elixir-lang.org/images/logo.png",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: nil
}
### Userを、Avater付きで、作成します。
### schemaで追加した「has_one :avatar」に値を指定する形です。
iex(5)> user = %User{name: "John Doe", email: "john.doe@example.com", avatar: avatar}
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:built, "users">,
avatar: %EctoAssoc.Avatar{
__meta__: #Ecto.Schema.Metadata<:built, "avatars">,
id: nil,
nick_name: "Elixir",
pic_url: "http://elixir-lang.org/images/logo.png",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: nil
},
email: "john.doe@example.com",
id: nil,
name: "John Doe"
}
### Userをテーブルに挿入します。
### Userがid=2で作成され、その中のid=1のAVaterがuser_id=2で作られているように見えるのがわかります。
iex(6)> user = Repo.insert!(user)
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
avatar: %EctoAssoc.Avatar{
__meta__: #Ecto.Schema.Metadata<:loaded, "avatars">,
id: 1,
nick_name: "Elixir",
pic_url: "http://elixir-lang.org/images/logo.png",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 2
},
email: "john.doe@example.com",
id: 2,
name: "John Doe"
}
### Userテーブルのエントリーを表示します。
### %User structの association(has_one :avatar)である :avatar をpreloadします。
### id=2のUserの中に埋め込まれる形でAvaterがpreloadされます。
iex(7)> Repo.all(User) |> Repo.preload(:avatar)
[
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
avatar: nil,
email: "john.doe@example.com",
id: 1,
name: "John Doe"
},
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
avatar: %EctoAssoc.Avatar{
__meta__: #Ecto.Schema.Metadata<:loaded, "avatars">,
id: 1,
nick_name: "Elixir",
pic_url: "http://elixir-lang.org/images/logo.png",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 2
},
email: "john.doe@example.com",
id: 2,
name: "John Doe"
}
]
2.One-to-many (User-Post)
次に One-to-many の Ecto Association を見ていきます。まず usersテーブルと postsテーブルを作成します。usersテーブルは前のものを再利用しますので、実際作るのはpostsのみになります。
2-1.Post
posts テーブルを作成する migrations を作ります。
mix ecto.gen.migration create_post
作成された migrations を以下のように修正します。
defmodule EctoAssoc.Repo.Migrations.CreatePost do
use Ecto.Migration
def change do
create table(:posts) do
add :header, :string
add :body, :string
end
end
end
Post schema を作成します
defmodule EctoAssoc.Post do
use Ecto.Schema
schema "posts" do
field :header, :string
field :body, :string
end
end
以上で準備が整いましたので、テーブルを作成したいと思います。
mix ecto.migrate
2-2.Associationsを追加
postsテーブルを変更する migration を作成します。
mix ecto.gen.migration post_belongs_to_user
どのように変更するかを記述します。ここでは user_id を users テーブルを参照する外部キーとして追加するように変更します。
defmodule EctoAssoc.Repo.Migrations.PostBelongsToUser do
use Ecto.Migration
def change do
alter table(:posts) do
add :user_id, references(:users)
end
end
end
Post schema を修正(追加)します。ここでは belongs_to field を追加します。これにより post.user で users テーブルにアクセスできるようになります。
defmodule EctoAssoc.Post do
use Ecto.Schema
schema "posts" do
field :header, :string
field :body, :string
belongs_to :user, EctoAssoc.User # 追加
end
end
User schema を修正(追加)します。これによる usersテーブルの変更はありませんが、user.posts によって posts テーブルへアクセスできるようになります。
defmodule EctoAssoc.User do
use Ecto.Schema
schema "users" do
field :name, :string
field :email, :string
has_many :posts, EctoAssoc.Post # 追加
end
end
migrate を実行して、実際にテーブルを変更します。
mix ecto.migrate
2-3.【オマケ】psqlコマンドでテーブルを確認
ここまでのDBの様子を、psqlコマンドで確認しておきます。Ecto.Repoを通さずに、生のテーブルも確認しておきたいと思います。
### psqlを起動します。
sudo -u postgres psql
### 以下のコマンドでDB名 ecto_assoc_repo を確認します。
postgres=# \l
### DB ecto_assoc_repo に接続します。
postgres=# \c ecto_assoc_repo
### テーブル一覧を表示します。
ecto_assoc_repo=# \z
Access privileges
Schema | Name | Type | Access privileges | Column access privi
leges
--------+-------------------+----------+-------------------+--------------------
------
public | avatars | table | |
public | avatars_id_seq | sequence | |
public | posts | table | |
public | posts_id_seq | sequence | |
public | schema_migrations | table | |
public | users | table | |
public | users_id_seq | sequence | |
(7 rows)
### postsテーブルの詳細を表示します。
ecto_assoc_repo=# \d posts
Table "public.posts"
Column | Type | Modifiers
---------+------------------------+---------------------------------------------
-------
id | bigint | not null default nextval('posts_id_seq'::reg
class)
header | character varying(255) |
body | character varying(255) |
user_id | bigint |
Indexes:
"posts_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"posts_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id)
2-4.実行結果の確認
iex -S mix
### Repo, User, Postモジュール名の短縮でアクセス可能にします。
iex(1)> alias EctoAssoc.{Repo, User, Post}
[EctoAssoc.Repo, EctoAssoc.User, EctoAssoc.Post]
### Userを、Post無しで、作成します。
iex(2)> user = %User{name: "John Doe", email: "john.doe@example.com"}
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:built, "users">,
email: "john.doe@example.com",
id: nil,
name: "John Doe",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
### Userをテーブルに挿入します。insert!(ビックリマーク)は例外を発生します。
### この時点で初めてUserはテーブルで実体化され、idが決まります。
iex(3)> user = Repo.insert!(user)
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "john.doe@example.com",
id: 1,
name: "John Doe",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
### postを、Userに関連付ける形で、作成します。Ecto.build_assoc/3 を使います。
### schemaで追加した「has_many :posts」に値を指定する形です。
### もちろん one_to_one で行った方法でも同じことが可能です。
iex(4)> post = Ecto.build_assoc(user, :posts, %{header: "Clickbait header", body: "No real content"})
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:built, "posts">,
body: "No real content",
header: "Clickbait header",
id: nil,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
### postをテーブルに挿入します。insert!(ビックリマーク)は例外を発生します。
### この時点で初めてpostはテーブルで実体化され、idが決まります。user_idが関連付けられます。
iex(5)> Repo.insert!(post)
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
### 2つめのpostを、Userに関連付ける形で、作成します。
### schemaで追加した「has_many :posts」に値を指定する形です。
iex(6)> post = Ecto.build_assoc(user, :posts, %{header: "5 ways to improve your Ecto", body: "Add url of this tutorial"})
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:built, "posts">,
body: "Add url of this tutorial",
header: "5 ways to improve your Ecto",
id: nil,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
### 2つめのpostをテーブルに挿入します。
### この時点で初めてpostはテーブルで実体化され、idが決まります。user_idが関連付けられます。
iex(7)> Repo.insert!(post)
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "Add url of this tutorial",
header: "5 ways to improve your Ecto",
id: 2,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
### Userテーブルのエントリーを表示します。
### %User structの association(has_many :posts)である :posts をpreloadします。
### :postsは必要に応じて自動的にlazy-loadされることは無く、明示的にpreloadする必要があります。
### id=1のUserの中にpostsが埋め込まれる形になります。
iex(8)> Repo.get(User, user.id) |> Repo.preload(:posts)
%EctoAssoc.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "john.doe@example.com",
id: 1,
name: "John Doe",
posts: [
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
},
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "Add url of this tutorial",
header: "5 ways to improve your Ecto",
id: 2,
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
]
}
iex(9)>
3.Many-to-many (Post-Tag)
次に Many-to-many の Ecto Association を見ていきます。まずpostsテーブルとtagsテーブルを作成します。postsテーブルは前のものを再利用しますので、実際作るのはtagsのみになります。
3-1.Tag
tags テーブルを作成する migrations を作ります。
mix ecto.gen.migration create_tag
作成された migrations を以下のように修正します。
defmodule EctoAssoc.Repo.Migrations.CreateTag do
use Ecto.Migration
def change do
create table(:tags) do
add :name, :string
end
end
end
Tag schema を作成します
defmodule EctoAssoc.Tag do
use Ecto.Schema
schema "tags" do
field :name, :string
end
end
以上で準備が整いましたので、テーブルを作成したいと思います。
mix ecto.migrate
3-2.Associationsを追加
Many-to-many の関係を記述するためには、これまでのように既存のテーブルを変更するのではなく、新しいテーブル posts_tags を追加します。posts_tagsテーブルを作成するmigrationを作成します。
mix ecto.gen.migration create_posts_tags
どのように変更するかを記述します。
defmodule EctoAssoc.Repo.Migrations.CreatePostsTags do
use Ecto.Migration
def change do
create table(:posts_tags) do
add :tag_id, references(:tags)
add :post_id, references(:posts)
end
create unique_index(:posts_tags, [:tag_id, :post_id])
end
end
Post schema を修正(追加)します。
defmodule EctoAssoc.Post do
use Ecto.Schema
schema "posts" do
field :header, :string
field :body, :string
many_to_many :tags, EctoAssoc.Tag, join_through: "posts_tags" # 追加
end
end
Tag schema を修正(追加)します。
defmodule EctoAssoc.Tag do
use Ecto.Schema
schema "tags" do
field :name, :string
many_to_many :posts, EctoAssoc.Post, join_through: "posts_tags" # 追加
end
end
migrate を実行して、実際にテーブルを追加します。
mix ecto.migrate
##3-3.実行結果の確認
iex -S mix
### Repo, Post、Tagモジュール名の短縮でアクセス可能にします。
iex(1)> alias EctoAssoc.{Repo, Tag, Post}
[EctoAssoc.Repo, EctoAssoc.Tag, EctoAssoc.Post]
まずTagとPostを作ります。
### Tagをテーブルに挿入します。 3つのtag "clickbait"と"misc"、"ecto"を挿入します。
iex(2)> clickbait_tag = Repo.insert! %Tag{name: "clickbait"}
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 1,
name: "clickbait",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
iex(3)> misc_tag = Repo.insert! %Tag{name: "misc"}
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 2,
name: "misc",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
iex(4)> ecto_tag = Repo.insert! %Tag{name: "ecto"}
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 3,
name: "ecto",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
### Postを作成します。
iex(5)> post = %Post{header: "Clickbait header", body: "No real content"}
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:built, "posts">,
body: "No real content",
header: "Clickbait header",
id: nil,
tags: #Ecto.Association.NotLoaded<association :tags is not loaded>
}
### postをテーブルに挿入します。
iex(6)> post = Repo.insert!(post)
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
tags: #Ecto.Association.NotLoaded<association :tags is not loaded>
}
以上でTagとPostを作りました。しかしAssociationsは作成していません。psqlで現状を確認してみましょう。
ecto_assoc_repo=# select * from tags;
id | name
----+-----------
1 | clickbait
2 | misc
3 | ecto
(3 rows)
ecto_assoc_repo=# select * from posts;
id | header | body | user_id
----+------------------+-----------------+---------
1 | Clickbait header | No real content |
(1 row)
ecto_assoc_repo=# select * from posts_tags; # Associationsは空です。
id | tag_id | post_id
----+--------+---------
(0 rows)
それではAssociationsを作成していきましょう。Ecto.Changesetを使いますので少し複雑になります。Ecto.Changesetを使うメリットは関連付けられたデータの変更に対して責任を持ってもらえることです。
### postをChangesetでラッピングします。
iex(7)> post_changeset = post |> Repo.preload(:tags) |> Ecto.Changeset.change
#Ecto.Changeset<action: nil, changes: %{}, errors: [], data: #EctoAssoc.Post<>,
valid?: true>
### put_assoc は association を change として changeset の中に入れます。
### ここでassociationは以下のPost schemaを指します。
### Post schema : many_to_many :tags, EctoAssoc.Tag, join_through: "posts_tags"
iex(8)> post_with_tags = Ecto.Changeset.put_assoc(post_changeset, :tags, [clickbait_tag, misc_tag])
#Ecto.Changeset<
action: nil,
changes: %{
tags: [
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #EctoAssoc.Tag<>, valid?: true>,
#Ecto.Changeset<action: :update, changes: %{}, errors: [],
data: #EctoAssoc.Tag<>, valid?: true>
]
},
errors: [],
data: #EctoAssoc.Post<>,
valid?: true
>
さて現状でもposts_tagsテーブルは空ですが、以下のコマンドでposts_tagsが更新されます。
iex(9)> post = Repo.update!(post_with_tags)
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
tags: [
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 1,
name: "clickbait",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
},
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 2,
name: "misc",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
]
}
posts_tagsテーブルを確認すると確かにエントリーがあります。つまりPost schemaのmany_to_manyの定義から、posts_tagsテーブルにpost_id=1の2エントリー (tag_id=1, post_id=1)と(tag_id=2, post_id=1)が挿入されたのを確認できます。
ecto_assoc_repo=# select * from posts_tags;
id | tag_id | post_id
----+--------+---------
1 | 1 | 1
2 | 2 | 1
(2 rows)
それではMany-manyの関係性を実際に確かめてみましょう。
iex(10)> post = Repo.get(Post, post.id) |> Repo.preload(:tags)
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
tags: [
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 1,
name: "clickbait",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
},
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 2,
name: "misc",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
]
}
iex(11)> post.header
"Clickbait header"
iex(12)> post.body
"No real content"
iex(13)> post.tags
[
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 1,
name: "clickbait",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
},
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 2,
name: "misc",
posts: #Ecto.Association.NotLoaded<association :posts is not loaded>
}
]
iex(14)> Enum.map(post.tags, & &1.name)
["clickbait", "misc"]
逆方向も確かめておきます。
iex(15)> tag = Repo.get(Tag, 1) |> Repo.preload(:posts)
%EctoAssoc.Tag{
__meta__: #Ecto.Schema.Metadata<:loaded, "tags">,
id: 1,
name: "clickbait",
posts: [
%EctoAssoc.Post{
__meta__: #Ecto.Schema.Metadata<:loaded, "posts">,
body: "No real content",
header: "Clickbait header",
id: 1,
tags: #Ecto.Association.NotLoaded<association :tags is not loaded>
}
]
}
以上で終わりますが、最後にオマケです。
4.【オマケ】ecto関連のコマンド
開発時は、以下のようにしてDBをクリアして、再度作り直すことができます。
mix ecto.drop
mix ecto.create
mix ecto.migrate
mix help でecto関連のコマンドを確認できます。英語で出力されていますが、まあ仕方ないですね。
# mix help | grep ecto
mix ecto # Prints Ecto help information
mix ecto.create # Creates the repository storage
mix ecto.drop # Drops the repository storage
mix ecto.dump # Dumps the repository database structure
mix ecto.gen.migration # Generates a new migration for the repo
mix ecto.gen.repo # Generates a new repository
mix ecto.load # Loads previously dumped database structure
mix ecto.migrate # Runs the repository migrations
mix ecto.migrations # Displays the repository migration status
mix ecto.rollback # Rolls back the repository migrations
mix phx.new.ecto # Creates a new Ecto project within an umbrella project
以上です。
■ Elixir/Phoenixの基礎についてまとめた過去記事
Elixir Ecto チュートリアル - Qiita
Elixir Ecto のまとめ - Qiita
Elixir Ecto Association - Qiita
Phoenix1.6の基本的な仕組み - Qiita