#elixirlang #ecto 관계를 여러 번 타는 건 ecto가 알아서 하고 우리는 through 옵션으로 한 번에 접근하자
2020-08-01
schema "artists" do
field(:name)
field(:birth_date, :date)
field(:death_date, :date)
timestamps()
has_many(:albums, Album, on_replace: :nilify)
end
schema "albums" do
field(:title, :string)
timestamps()
belongs_to(:artist, Artist)
has_many(:tracks, Track)
end
schema "tracks" do
field(:title, :string)
field(:duration, :integer)
field(:duration_string, :string, virtual: true)
field(:index, :integer)
field(:number_of_plays, :integer)
timestamps()
belongs_to(:album, Album)
end
DB 예제 단골이시다. 아티스트와 앨범은 일대다(one-to-many) 관계이고 앨범과 트랙의 관계도 일대다 관계다. 아티스트의 트랙 전부를 쿼리하려면 어떻게 하면 될까?
iex> import Ecto.Query iex> (from(a in MusicDB.Artist, where: a.name == "Bobby Hutcherson") ...> |> MusicDB.Repo.all() ...> |> MusicDB.Repo.preload([:albums]) ...> |> Enum.flat_map(& &1.albums) ...> |> MusicDB.Repo.preload([:tracks]) ...> |> Enum.flat_map(& &1.tracks)) [ %MusicDB.Track{ __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">, album: #Ecto.Association.NotLoaded<association :album is not loaded>, album_id: 5, duration: 761, duration_string: nil, id: 30, index: 1, inserted_at: ~N[2020-07-20 11:04:28], number_of_plays: 0, title: "Anton's Ball", updated_at: ~N[2020-07-20 11:04:28] }, # ... %MusicDB.Track{ __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">, album: #Ecto.Association.NotLoaded<association :album is not loaded>, album_id: 5, duration: 844, duration_string: nil, id: 33, index: 4, inserted_at: ~N[2020-07-20 11:04:29], number_of_plays: 0, title: "Song Of Songs", updated_at: ~N[2020-07-20 11:04:29] } ]
아티스트와 관계(relationship)된 앨범을 읽어오고 다시 앨범과 관계된 트랙을 읽어오면 된다. Ecto.Repo.preload/2 함수는 관계 데이터를 읽어오는 함수다. 아티스트를 쿼리해서 읽어온다고 하더라도 preload 함수를 호출해 읽어오기 전까진 관계된 앨범 데이터를 읽어오지 않는다.
schema "artists" do
field(:name)
field(:birth_date, :date)
field(:death_date, :date)
timestamps()
has_many(:albums, Album, on_replace: :nilify)
has_many(:tracks, through: [:albums, :tracks]) #-- 1
end
1번 코드처럼 has_many/3, has_one/3 함수 through 옵션을 사용하면 관계를 타고 들어가야 하는 데이터를 쉽게 쿼리할 수 있다. 아티스트의 tracks 관계를 가져오면 :albums, :tracks 관계 데이터를 차례로 가져온다.
iex> import Ecto.Query iex> (from(a in MusicDB.Artist, where: a.name == "Bobby Hutcherson") ...> |> MusicDB.Repo.all() ...>|> MusicDB.Repo.preload([:tracks]) ...>|> Enum.flat_map(& &1.tracks))
MusicDB.Repo.preload([:tracks])
한 번으로 가져올 수 있다. programming ecto 책에서 배웠다.

- tags:
- elixirlang 39
- ecto 3
@ohyecloudy
,ohyecloudy@gmail.com