Associate three schemes using has_one/belongs_to

Hello,

I want to associate three schemas, in one transaction. The Schema A through cast_assoc/3 insert an id in the table B, this is working, but the Schema B is not insert his id in the table C. The association cas_assoc/3 is well implemented for has_one/belongs_to ?.

I will appreciate your help

Schema A
has_many :b, B
|> cast_assoc(:b, with: &App.ABC.B.changeset/2)
Schema B
belongs_to :a, A
has_one :c, C
|> cast_assoc(:c, with: &App.ABC.C.changeset/2)
Schema C
belongs_to :b, B

How do your params map look like?

It should look something like this:

params = 
%{
  "b" => [
           %{ "c" => %{ } },
           %{ "c" => %{ } },
           %{ "c" => %{ } }
         ]
}

Please remember that cast_assoc should be used when working with parent and child association at once. so you should have input with similar structure.

Thank’s Joaquin,

The parameters is OK, and Schema C is child the B. I can’t understand

I just tried this example and worked:

This are the shemas:

Artist:

defmodule Music.Artist do
  use Ecto.Schema
  import Ecto.Changeset
  alias Music.{Artist, Album}

  schema "artists" do
    field(:name)
    timestamps()

    has_many(:albums, Album)  
  end

  def changeset(%Artist{} = artist, params) do
    artist
    |> cast(params, [:name])
    |> validate_required([:name])
    |> cast_assoc(:albums)
  end
end

Album

defmodule Music.Album do
  use Ecto.Schema
  import Ecto.Changeset
  alias Music.{Artist, Track, Genre}

  schema "albums" do
    field(:title, :string)
    timestamps()

    belongs_to(:artist, Artist)
    has_many(:tracks, Track)
  end

  def changeset(album, params) do
    album
    |> cast(params, [:title])
    |> validate_required([:title])
    |> cast_assoc(:tracks)
  end
end

Track

defmodule Music.Track do
  use Ecto.Schema
  import Ecto.Changeset
  alias Music.Album

  schema "tracks" do
    field(:title, :string)
    timestamps()

    belongs_to(:album, Album)
  end

  def changeset(track, params) do
    track
    |> cast(params, [:title])
    |> validate_required([:title])
  end

end

This are the parameters

params = 
  %{
       "name" => "My name",
       "albums" => [
                    %{
                      "title" => "El Primero",
                      "tracks" => [
                                   %{"title" => "Mi primer cancion"}
                                  ]
                     },
                    ]
   }

This is the iex:

iex(4)> Music.Artist.changeset(%Artist{}, params)

This is the result:

#Ecto.Changeset<
  action: nil,
  changes: %{
    albums: [
      #Ecto.Changeset<
        action: :insert,
        changes: %{
          title: "El Primero",
          tracks: [
            #Ecto.Changeset<
              action: :insert,
              changes: %{title: "Mi primer cancion"},
              errors: [],
              data: #Music.Track<>,
              valid?: true
            >
          ]
        },
        errors: [],
        data: #Music.Album<>,
        valid?: true
      >
    ],
    name: "My name"
  },
  errors: [],
  data: #Music.Artist<>,
  valid?: true
>

Hope this helps.

Best regards,

1 Like

Thank you Joaquin, you are right.

Saludos

1 Like