#elixirlang 외래 키는 스키마에 정의하고 ecto 연관 함수로 외래 키를 직접 쓰지 말자
연관(associations) 함수를 사용하면 외래 키(foreign key)를 직접 안 넘겨도 된다. ecto 연관 함수를 쓰면 스키마(schema)에 외래 키를 정의하기에 외래 키를 직접 넘기지 않아도 된다. Programming Phoenix 1.4 책에서 본 온 소스 코드를 간단하게 수정했다.
defmodule User do
use Ecto.Schema
schema "users" do
field(:name, :string)
has_many(:videos, Video)
end
end
defmodule Video do
use Ecto.Schema
schema "videos" do
field(:title, :string)
belongs_to(:user, User)
end
end
테스트를 위해 스키마를 먼저 정의한다. User와 Video에 일대다 관계(one-to-many relationship)를 설정한다. belongs_to/3 매크로로 외래 키를 정의한다. 옵션을 넘기지 않으면 _id
접미사를 붙여서 키를 정의한다. 즉 user_id
키를 정의한다.
iex> {:ok, user} = Repo.insert(%User{name: "ohyecloudy"})
{:ok,
%User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 1,
name: "ohyecloudy",
videos: #Ecto.Association.NotLoaded<association :videos is not loaded>
}}
iex> {:ok, video} = Repo.insert(%Video{title: "video 1", user_id: user.id})
{:ok,
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 1,
title: "video 1",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}}
user_id: user.id
키와 값을 넘겨 관계를 만든다. 이렇게 직접 외래 키를 넣는 방법 대신에 스키마에서 가져오게 할 수도 있다.
iex> {:ok, video} = Ecto.build_assoc(user, :videos, %{title: "video 2"}) |> Repo.insert()
{:ok,
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 2,
title: "video 2",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}}
Ecto.build_assoc/3 함수를 사용하면 외래 키 이름을 스키마에서 가져와서 넣는다. 외래 키 이름이 뭔지 신경 쓸 필요 없이 스키마에 정의한 대로 관계를 만든다.
iex> import Ecto.Query
iex> Repo.all(from v in Video, where: v.user_id == ^user.id)
[
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 1,
title: "video 1",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
},
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 2,
title: "video 2",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
]
where
절에 user_id
컬럼 값을 검사해서 video를 찾을 수 있다.
iex> Ecto.assoc(user, :videos) |> Repo.all()
[
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 1,
title: "video 1",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
},
%Video{
__meta__: #Ecto.Schema.Metadata<:loaded, "videos">,
id: 2,
title: "video 2",
user: #Ecto.Association.NotLoaded<association :user is not loaded>,
user_id: 1
}
]
Ecto.assoc/2 함수를 사용하면 더 간단하게 쿼리(query) 문을 만들 수 있다.
ecto 연관 함수를 사용하면 스키마에 정의한 외래 키를 사용해 더 편하게 조회 및 삽입을 할 수 있다.