How to check the relationship between the functions in Ecto

Hello , I have a bug in all of my project, for example , Please think , I have 3 schema’s , these code are :

schema "cms_learn_category" do
  field :title, :string
  field :status, :boolean
  field :language, :string
  has_many :cms_learn_headlines, TrangellCmsService.Cms.Db.LearnHeadlines,  foreign_key: :cms_learn_category_id
  has_many :cms_learn_post, TrangellCmsService.Cms.Db.LearnPost,  foreign_key: :cms_learn_category_id
end

schema "cms_learn_headlines" do
  field :title, :string
  field :status, :boolean
  belongs_to :cms_learn_category, TrangellCmsService.Cms.Db.LearnCategory, foreign_key: :cms_learn_category_id, type: :binary_id
  has_many :cms_learn_post, TrangellCmsService.Cms.Db.LearnPost,  foreign_key: :cms_learn_headlines_id
end

schema "cms_learn_post" do
  field :title, :string
  field :status, :boolean
  belongs_to :cms_learn_category, TrangellCmsService.Cms.Db.LearnCategory, foreign_key: :cms_learn_category_id, type: :binary_id
  belongs_to :cms_learn_headlines, TrangellCmsService.Cms.Db.LearnHeadlines, foreign_key: :cms_learn_headlines_id, type: :binary_id
end

if I use cms_learn_post. cms_learn_category and cms_learn_postand. cms_learn_headlines which aren’t related, this data will still send to database , but I don’t want to.

Does elixir Ecto have a relation factions checker to check this before sending to db?

false :

true :

Can you please reword your problem? I can’t make any sense of your writing, especially after you have given two identical pictures of which one shows a good and another a bad situation while still being the same pictures.

Perhaps give some more example code what you mean by “gets submitted”, or “post POST”.

Also what is not related? As I understand the schemas you have shown, the scms_learn_post belongs to cms_learn_category and cms_learn_headlines, so they are related according to my understanding of the schema.

1 Like
  • Even if they are related, the same {cms_learn_category,cms_learn_headlines} could be potentially used by many cms_learn_post - so how do you know which cms_learn_post this relates to?
  • How is it that the web page can even submit unrelated {cms_learn_category,cms_learn_headlines}?

Sounds more like a design problem, not an Ecto problem.

1 Like

Im sorry I can’t speak english better than, but I tray again.

please see these data :

I have 2 cms_learn_category as follows :

id: 1 , title: "category-one"
id:2 , title: "category-two"

and also, I have 2 cms_learn_headlines

id: 1 , title: "headlines-one" which was related with category id 1
id:2 , title: "headlines-two" which was related with category id 1

now I want to create post , at first I write the true way

post_id: 1 , title: "post-one", rel.category id 1 and rel.headlines id 1

so far all of these are true, but if I do false way these send to my db again, for example

post_id: 1 , title: "post-one", rel.category id 2 and rel.headlines id 1

rel.category id 2 isn’t relation with rel.headlines id 1 , but they are saved in my database, how do I stop to save this and check?

Ecto.Changeset.check_constraint/3

1 Like

which schema should I put in ? Post schema or Category Schema or Headlines schema ?

I added in Post changes but I have an error :

|> check_constraint(:cms_learn_category_id)
|> check_constraint(:cms_learn_headlines_id)

error

** (exit) an exception was raised:
    ** (ArgumentError) must supply the name of the constraint

my Schema : https://gist.github.com/shahryarjb/9317eb90c12ea77c32cf75b12a90a6c5

belongs_to :cms_learn_category bothers me. cms_learn_category is implied via cms_learn_headlines - so cms_learn_category_id is redundant.

belongs_to :cms_learn_category expresses a relationship to cms_learn_category that is independent of cms_learn_headlines.

id: 1, title: "category-one"
id: 2, title: "category-two"
id: 1, title: "headlines-one", cms_learn_category_id: 1
id: 2, title: "headlines-two", cms_learn_category_id: 1
id: 1 , title: "post-one", cms_learn_headlines_id: 1 # impossible to set incorrect category which is found on cms_learn_headlines
1 Like

I did this, because I need this

	def load_learn_post_with_category_headlines(seo_alias_link, group_acl) do
		with %{category_seo_alias_link: category_alias_link} = post <- load_learn_post(seo_alias_link, group_acl),
				 	{:ok, headlines} <- load_category_with_headlines(category_alias_link, group_acl) do
						%{post: post, headlines: headlines}
		else
			[] 			-> {:error, :wrong_data}
			nil 		-> 	{:error, :wrong_data}
			:error 	->  {:error, :wrong_data}
		end
	end

in that function I have 3 options like Category info — Headlines info — and Post info, when I take or want a post, if I remove cms_learn_category, how will I take those ?

You can get all those in one fell swoop with nested preloads.

cms_learn_post -> cms_learn_headlines -> cms_learn_category -> cms_learn_headlines

An equivalent example with

Track -> Album -> Artist -> Album

iex(1)> alias MusicDB.{Repo,Album,Track}
[MusicDB.Repo, MusicDB.Album, MusicDB.Track]
iex(2)> import Ecto.Query
Ecto.Query
iex(3)> track_id = 29
29
iex(4)> query = from(t in Track, [
...(4)>  preload: [{:album,[{:artist,:albums}]}],
...(4)>  where: t.id == ^track_id
...(4)> ])
#Ecto.Query<from t in MusicDB.Track, where: t.id == ^29,
 preload: [album: [artist: [:albums]]]>
iex(5)> Repo.all(query)

17:24:06.532 [debug] QUERY OK source="tracks" db=2.4ms decode=1.6ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id" FROM "tracks" AS t0 WHERE (t0."id" = $1) [29]
 
17:24:06.538 [debug] QUERY OK source="albums" db=1.6ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."id" FROM "albums" AS a0 WHERE (a0."id" = $1) [4]
 
17:24:06.540 [debug] QUERY OK source="artists" db=0.8ms
SELECT a0."id", a0."name", a0."birth_date", a0."death_date", a0."inserted_at", a0."updated_at", a0."id" FROM "artists" AS a0 WHERE (a0."id" = $1) [2]
 
17:24:06.543 [debug] QUERY OK source="albums" db=1.8ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."artist_id" FROM "albums" AS a0 WHERE (a0."artist_id" = $1) ORDER BY a0."artist_id" [2]
[
  %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: %MusicDB.Album{
      __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
      artist: %MusicDB.Artist{
        __meta__: #Ecto.Schema.Metadata<:loaded, "artists">,
        albums: [
          %MusicDB.Album{
            __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
            artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
            artist_id: 2,
            genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
            id: 4,
            inserted_at: ~N[2018-06-16 20:29:41.493342],
            title: "Portrait In Jazz",
            tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
            updated_at: ~N[2018-06-16 20:29:41.493348]
          },
          %MusicDB.Album{
            __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
            artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
            artist_id: 2,
            genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
            id: 3,
            inserted_at: ~N[2018-06-16 20:29:41.483682],
            title: "You Must Believe In Spring",
            tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
            updated_at: ~N[2018-06-16 20:29:41.483713]
          }
        ],
        birth_date: nil,
        death_date: nil,
        id: 2,
        inserted_at: ~N[2018-06-16 20:29:41.481934],
        name: "Bill Evans",
        tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
        updated_at: ~N[2018-06-16 20:29:41.481940]
      },
      artist_id: 2,
      genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
      id: 4,
      inserted_at: ~N[2018-06-16 20:29:41.493342],
      title: "Portrait In Jazz",
      tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
      updated_at: ~N[2018-06-16 20:29:41.493348]
    },
    album_id: 4,
    duration: 325,
    duration_string: nil,
    id: 29,
    index: 9,
    inserted_at: ~N[2018-06-16 20:29:41.499008],
    number_of_plays: 0,
    title: "Blue In Green",
    updated_at: ~N[2018-06-16 20:29:41.499013]
  }
]
iex(6)>
1 Like

Thank you for giving your time to me, I didn’t use preload because I couldn’t convert that to json or map easily. but I think I’m forced to use this .

:icon_question:

iex(5)> results = Repo.one(query)

18:49:30.301 [debug] QUERY OK source="tracks" db=2.5ms decode=1.7ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id" FROM "tracks" AS t0 WHERE (t0."id" = $1) [29]
 
18:49:30.308 [debug] QUERY OK source="albums" db=1.7ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."id" FROM "albums" AS a0 WHERE (a0."id" = $1) [4]
 
18:49:30.310 [debug] QUERY OK source="artists" db=1.7ms
SELECT a0."id", a0."name", a0."birth_date", a0."death_date", a0."inserted_at", a0."updated_at", a0."id" FROM "artists" AS a0 WHERE (a0."id" = $1) [2]
 
18:49:30.314 [debug] QUERY OK source="albums" db=1.7ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."artist_id" FROM "albums" AS a0 WHERE (a0."artist_id" = $1) ORDER BY a0."artist_id" [2]
%MusicDB.Track{
  __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
  album: %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: %MusicDB.Artist{
      __meta__: #Ecto.Schema.Metadata<:loaded, "artists">,
      albums: [
        %MusicDB.Album{
          __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
          artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
          artist_id: 2,
          genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
          id: 4,
          inserted_at: ~N[2018-06-16 20:29:41.493342],
          title: "Portrait In Jazz",
          tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
          updated_at: ~N[2018-06-16 20:29:41.493348]
        },
        %MusicDB.Album{
          __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
          artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
          artist_id: 2,
          genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
          id: 3,
          inserted_at: ~N[2018-06-16 20:29:41.483682],
          title: "You Must Believe In Spring",
          tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
          updated_at: ~N[2018-06-16 20:29:41.483713]
        }
      ],
      birth_date: nil,
      death_date: nil,
      id: 2,
      inserted_at: ~N[2018-06-16 20:29:41.481934],
      name: "Bill Evans",
      tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
      updated_at: ~N[2018-06-16 20:29:41.481940]
    },
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 4,
    inserted_at: ~N[2018-06-16 20:29:41.493342],
    title: "Portrait In Jazz",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2018-06-16 20:29:41.493348]
  },
  album_id: 4,
  duration: 325,
  duration_string: nil,
  id: 29,
  index: 9,
  inserted_at: ~N[2018-06-16 20:29:41.499008],
  number_of_plays: 0,
  title: "Blue In Green",
  updated_at: ~N[2018-06-16 20:29:41.499013]
}
iex(6)> forget_assoc = fn ecto_struct, field, cardinality ->
...(6)>   not_loaded = %Ecto.Association.NotLoaded{
...(6)>     __field__: field,
...(6)>     __owner__: ecto_struct.__struct__,
...(6)>     __cardinality__: cardinality
...(6)>   }
...(6)>   Map.put(ecto_struct, field, not_loaded)
...(6)> end
#Function<18.127694169/3 in :erl_eval.expr/5>
iex(7)> albums = results.album.artist.albums
[
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 4,
    inserted_at: ~N[2018-06-16 20:29:41.493342],
    title: "Portrait In Jazz",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2018-06-16 20:29:41.493348]
  },
  %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 3,
    inserted_at: ~N[2018-06-16 20:29:41.483682],
    title: "You Must Believe In Spring",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2018-06-16 20:29:41.483713]
  }
]
iex(8)> track = forget_assoc.(results, :album, :one)
%MusicDB.Track{
  __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
  album: #Ecto.Association.NotLoaded<association :album is not loaded>,
  album_id: 4,
  duration: 325,
  duration_string: nil,
  id: 29,
  index: 9,
  inserted_at: ~N[2018-06-16 20:29:41.499008],
  number_of_plays: 0,
  title: "Blue In Green",
  updated_at: ~N[2018-06-16 20:29:41.499013]
}
iex(9)> result = %{track: track, albums: albums}
%{
  albums: [
    %MusicDB.Album{
      __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
      artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
      artist_id: 2,
      genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
      id: 4,
      inserted_at: ~N[2018-06-16 20:29:41.493342],
      title: "Portrait In Jazz",
      tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
      updated_at: ~N[2018-06-16 20:29:41.493348]
    },
    %MusicDB.Album{
      __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
      artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
      artist_id: 2,
      genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
      id: 3,
      inserted_at: ~N[2018-06-16 20:29:41.483682],
      title: "You Must Believe In Spring",
      tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
      updated_at: ~N[2018-06-16 20:29:41.483713]
    }
  ],
  track: %MusicDB.Track{
    __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
    album: #Ecto.Association.NotLoaded<association :album is not loaded>,
    album_id: 4,
    duration: 325,
    duration_string: nil,
    id: 29,
    index: 9,
    inserted_at: ~N[2018-06-16 20:29:41.499008],
    number_of_plays: 0,
    title: "Blue In Green",
    updated_at: ~N[2018-06-16 20:29:41.499013]
  }
}
iex(10)> 

I mean , I have a problem to convert, fo example

artist: %MusicDB.Artist{
      __meta__: #Ecto.Schema.Metadata<:loaded, "artists">,
or
%MusicDB.Album{
          __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
          artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,

when Poison Json in controller wants to convert , it shows me the error which it can’t

You need a JSON view

https://hexdocs.pm/phoenix/views.html#rendering-json

1 Like
iex(1)> alias MusicDB.{Repo,Album,Track}
[MusicDB.Repo, MusicDB.Album, MusicDB.Track]
iex(2)> import Ecto.Query
Ecto.Query
iex(3)> prep_album = fn row -> %{title: row.album_title} end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(4)> make_albums = fn rows -> Enum.map(rows, prep_album) end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(5)> make_track = fn rows ->
...(5)>   rows
...(5)>   |> hd()
...(5)>   |> Map.take([:index, :title, :duration])
...(5)> end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(6)> make_result = fn
...(6)>   [] ->
...(6)>     nil
...(6)>   rows ->
...(6)>     %{track: make_track.(rows), albums: make_albums.(rows)}
...(6)> end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(7)> track_id = 29
29
iex(8)> query = from(t in Track, [
...(8)>  join: al in Album, on: t.album_id == al.id,
...(8)>  join: ar in Artist, on: al.artist_id == ar.id,
...(8)>  join: als in Album, on: ar.id == als.artist_id,
...(8)>  select: %{index: t.index, title: t.title, duration: t.duration, album_title: als.title},
...(8)>  where: t.id == ^track_id
...(8)> ])
#Ecto.Query<from t in MusicDB.Track, join: a0 in MusicDB.Album,
 on: t.album_id == a0.id, join: a1 in MusicDB.Artist, on: a0.artist_id == a1.id,
 join: a2 in MusicDB.Album, on: a1.id == a2.artist_id, where: t.id == ^29,
 select: %{index: t.index, title: t.title, duration: t.duration, album_title: a2.title}>
iex(9)> rows = Repo.all(query)

19:25:42.582 [debug] QUERY OK source="tracks" db=2.5ms
SELECT t0."index", t0."title", t0."duration", a3."title" FROM "tracks" AS t0 INNER JOIN "albums" AS a1 ON t0."album_id" = a1."id" INNER JOIN "artists" AS a2 ON a1."artist_id" = a2."id" INNER JOIN "albums" AS a3 ON a2."id" = a3."artist_id" WHERE (t0."id" = $1) [29]
[
  %{
    album_title: "Portrait In Jazz",
    duration: 325,
    index: 9,
    title: "Blue In Green"
  },
  %{
    album_title: "You Must Believe In Spring",
    duration: 325,
    index: 9,
    title: "Blue In Green"
  }
]
iex(10)> result = make_result.(rows)
%{
  albums: [%{title: "Portrait In Jazz"}, %{title: "You Must Believe In Spring"}],
  track: %{duration: 325, index: 9, title: "Blue In Green"}
}
iex(11)>  

or

iex(1)> alias MusicDB.{Repo,Album,Track}
[MusicDB.Repo, MusicDB.Album, MusicDB.Track]
iex(2)> import Ecto.Query
Ecto.Query
iex(3)> prep_album = fn album -> %{title: album.title} end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(4)> prep_track = fn track -> %{index: track.index, title: track.title, duration: track.duration} end 
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(5)> make_result = fn
...(5)>   nil ->
...(5)>     nil
...(5)>   row ->
...(5)>     %{track: prep_track.(row), albums: Enum.map(row.album.artist.albums, prep_album)}
...(5)> end
#Function<6.127694169/1 in :erl_eval.expr/5>
iex(6)> track_id = 29
29
iex(7)> query = from(t in Track, [
...(7)>  preload: [{:album, [{:artist, :albums}]}],
...(7)>  where: t.id == ^track_id
...(7)> ])
#Ecto.Query<from t in MusicDB.Track, where: t.id == ^29,
 preload: [album: [artist: [:albums]]]>
iex(8)> row = Repo.one(query)

19:56:53.613 [debug] QUERY OK source="tracks" db=2.6ms decode=1.8ms
SELECT t0."id", t0."title", t0."duration", t0."index", t0."number_of_plays", t0."inserted_at", t0."updated_at", t0."album_id" FROM "tracks" AS t0 WHERE (t0."id" = $1) [29]
 
19:56:53.620 [debug] QUERY OK source="albums" db=2.1ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."id" FROM "albums" AS a0 WHERE (a0."id" = $1) [4]
 
19:56:53.622 [debug] QUERY OK source="artists" db=1.7ms
SELECT a0."id", a0."name", a0."birth_date", a0."death_date", a0."inserted_at", a0."updated_at", a0."id" FROM "artists" AS a0 WHERE (a0."id" = $1) [2]
 
19:56:53.625 [debug] QUERY OK source="albums" db=0.9ms
SELECT a0."id", a0."title", a0."inserted_at", a0."updated_at", a0."artist_id", a0."artist_id" FROM "albums" AS a0 WHERE (a0."artist_id" = $1) ORDER BY a0."artist_id" [2]
%MusicDB.Track{
  __meta__: #Ecto.Schema.Metadata<:loaded, "tracks">,
  album: %MusicDB.Album{
    __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
    artist: %MusicDB.Artist{
      __meta__: #Ecto.Schema.Metadata<:loaded, "artists">,
      albums: [
        %MusicDB.Album{
          __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
          artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
          artist_id: 2,
          genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
          id: 4,
          inserted_at: ~N[2018-06-16 20:29:41.493342], 
          title: "Portrait In Jazz",
          tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
          updated_at: ~N[2018-06-16 20:29:41.493348]
        },
        %MusicDB.Album{
          __meta__: #Ecto.Schema.Metadata<:loaded, "albums">, 
          artist: #Ecto.Association.NotLoaded<association :artist is not loaded>,
          artist_id: 2,
          genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
          id: 3,
          inserted_at: ~N[2018-06-16 20:29:41.483682],
          title: "You Must Believe In Spring",
          tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
          updated_at: ~N[2018-06-16 20:29:41.483713]
        }
      ],
      birth_date: nil,
      death_date: nil,
      id: 2,
      inserted_at: ~N[2018-06-16 20:29:41.481934],
      name: "Bill Evans",
      tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
      updated_at: ~N[2018-06-16 20:29:41.481940]
    },
    artist_id: 2,
    genres: #Ecto.Association.NotLoaded<association :genres is not loaded>,
    id: 4,
    inserted_at: ~N[2018-06-16 20:29:41.493342],
    title: "Portrait In Jazz",
    tracks: #Ecto.Association.NotLoaded<association :tracks is not loaded>,
    updated_at: ~N[2018-06-16 20:29:41.493348]
  },
  album_id: 4,
  duration: 325,
  duration_string: nil,
  id: 29,
  index: 9,
  inserted_at: ~N[2018-06-16 20:29:41.499008],
  number_of_plays: 0,
  title: "Blue In Green",
  updated_at: ~N[2018-06-16 20:29:41.499013]
}
iex(9)> result = make_result.(row)
%{
  albums: [%{title: "Portrait In Jazz"}, %{title: "You Must Believe In Spring"}],
  track: %{duration: 325, index: 9, title: "Blue In Green"}
}
iex(10)> 
1 Like