# priv/repo/migrations/*_create_movies.exs
defmodule Example.Repo.Migrations.CreateMovies do
use Ecto.Migration
def change do
create table(:movies) do
add :title, :string
add :tagline, :string
end
end
end
“一对多”的 Schema
然后我们添加指定电影和角色之间的“一对多”关系的 schema。
# lib/example/movie.ex
defmodule Example.Movie do
use Ecto.Schema
schema "movies" do
field :title, :string
field :tagline, :string
has_many :characters, Example.Character
end
end
# priv/migrations/*_create_characters.exs
defmodule Example.Repo.Migrations.CreateCharacters do
use Ecto.Migration
def change do
create table(:characters) do
add :name, :string
add :movie_id, references(:movies)
end
end
end
“属于”的 Schema
我们的 schema 也要相应的定义角色“属于”它的电影的关系。
# lib/example/character.ex
defmodule Example.Character do
use Ecto.Schema
schema "characters" do
field :name, :string
belongs_to :movie, Example.Movie
end
end
# priv/repo/migrations/*_create_distributors.exs
defmodule Example.Repo.Migrations.CreateDistributors do
use Ecto.Migration
def change do
create table(:distributors) do
add :name, :string
add :movie_id, references(:movies)
end
end
end
# lib/example/distributor.ex
defmodule Example.Distributor do
use Ecto.Schema
schema "distributors" do
field :name, :string
belongs_to :movie, Example.Movie
end
end
接着,我们就可以把“一对一”关系添加到 Movie schema:
# lib/example/movie.ex
defmodule Example.Movie do
use Ecto.Schema
schema "movies" do
field :title, :string
field :tagline, :string
has_many :characters, Example.Character
has_one :distributor, Example.Distributor # I'm new!
end
end
一部电影可以有多个演员,一个演员可以出演多部电影。我们建立一个关联表来把 movies 和 actors 两个表关联起来实现这个关系。
首先,让我们生成 Actors migration:
mix ecto.gen.migration create_actors
定义 migration 内容:
# priv/migrations/*_create_actors.ex
defmodule Example.Repo.Migrations.Actors do
use Ecto.Migration
def change do
create table(:actors) do
add :name, :string
end
end
end
# priv/migrations/*_create_movies_actors.ex
defmodule Example.Repo.Migrations.CreateMoviesActors do
use Ecto.Migration
def change do
create table(:movies_actors) do
add :movie_id, references(:movies)
add :actor_id, references(:actors)
end
create unique_index(:movies_actors, [:movie_id, :actor_id])
end
end
接着,添加一个 many_to_many 宏到 Movie schema:
# lib/example/movie.ex
defmodule Example.Movie do
use Ecto.Schema
schema "movies" do
field :title, :string
field :tagline, :string
has_many :characters, Example.Character
has_one :distributor, Example.Distributor
many_to_many :actors, Example.Actor, join_through: "movies_actors" # I'm new!
end
end
最后,使用同样的 many_to_many 宏来定义我们的 Actor schema。
# lib/example/actor.ex
defmodule Example.Actor do
use Ecto.Schema
schema "actors" do
field :name, :string
many_to_many :movies, Example.Movie, join_through: "movies_actors"
end
end
iex> alias Example.{Movie, Character, Repo}
iex> movie = %Movie{title: "Ready Player One", tagline: "Something about video games"}
%Example.Movie{
__meta__: %Ecto.Schema.Metadata<:built, "movies">,
actors: %Ecto.Association.NotLoaded<association :actors is not loaded>,
characters: %Ecto.Association.NotLoaded<association :characters is not loaded>,
distributor: %Ecto.Association.NotLoaded<association :distributor is not loaded>,
id: nil,
tagline: "Something about video games",
title: "Ready Player One"
}
iex> movie = Repo.insert!(movie)
现在我们要创建相关的角色和保存到数据库里:
character = Ecto.build_assoc(movie, :characters, %{name: "Wade Watts"})
%Example.Character{
__meta__: %Ecto.Schema.Metadata<:built, "characters">,
id: nil,
movie: %Ecto.Association.NotLoaded<association :movie is not loaded>,
movie_id: 1,
name: "Wade Watts"
}
Repo.insert!(character)
%Example.Character{
__meta__: %Ecto.Schema.Metadata<:loaded, "characters">,
id: 1,
movie: %Ecto.Association.NotLoaded<association :movie is not loaded>,
movie_id: 1,
name: "Wade Watts"
}
要注意的是,因为 Movie schema 中的 has_many/3 宏指定了一部电影拥有多个 :characters,我们通过第二个参数传到 build_assoc/3 的关系的名字,就是 :characters。这样,我们就创建了一个把相应的电影 ID 设置到了 movie_id 的角色。