How do I insert items with associations into my database via iex?

N.B. I don’t seem to have permission to post in the Phoenix section :woman_shrugging:

Background

I’m learning Phoenix and my relational database skills are a bit rusty to say the least. I’m trying to create a data structure for creating musical chord progressions, whereby a Progression has a many to many association with a Chord. I’m loosely following this guide. To do this I have the following schema (please tell me if my design is generally awful by the way!):

# create_chords migration
defmodule Chordwitch.Repo.Migrations.CreateChords do
  use Ecto.Migration

  def change do
    create table(:chords) do
      add :numeral, :string
      add :extension, :string
      add :tonality, :string
      add :function, :string
      add :comments, :text
      add :altered, :boolean, default: false, null: false

      timestamps()
    end
  end
end
# lib/chords/chord.ex
defmodule Chordwitch.Chords.Chord do
  use Ecto.Schema
  import Ecto.Changeset
  alias Progressions.Progression

  schema "chords" do
    field :altered, :boolean, default: false
    field :comments, :string
    field :extension, :string
    field :function, :string
    field :numeral, :string
    field :tonality, Ecto.Enum, values: [:major, :minor, :neutral]
    many_to_many :progressions, Progression, join_through: "progression_chords"

    timestamps()
  end

  @doc false
  def changeset(chord, attrs) do
    chord
    |> cast(attrs, [:numeral, :extension, :tonality, :function, :comments, :altered])
    |> validate_required([:numeral, :extension, :tonality, :function, :comments, :altered])
  end
end

# create_progressions migration
defmodule Chordwitch.Repo.Migrations.CreateProgressions do
  use Ecto.Migration

  def change do
    create table(:progressions) do
      add :name, :string
      add :tags, {:array, :string}

      timestamps()
    end
  end
end
# lib/progressions/progression.ex
defmodule Chordwitch.Progressions.Progression do
  use Ecto.Schema
  import Ecto.Changeset
  alias Chords.Chord

  schema "progressions" do
    field :name, :string
    field :tags, {:array, :string}
    many_to_many :chords, Chord, join_through: "progression_chords"

    timestamps()
  end

  @doc false
  def changeset(progression, attrs) do
    progression
    |> cast(attrs, [:name, :tags])
    |> validate_required([:name, :tags])
  end
end

As you can see I’ve also created a relationship table for progression_chords with many_to_many associations in both my chord and progression schema. The migration for progression_chords looks like this:

defmodule Chordwitch.Repo.Migrations.CreateProgressionChords do
  use Ecto.Migration

  def change do
    create table(:progression_chords) do
      add :progression_id, references(:progressions)
      add :chord_id, references(:chords)
      add :index, :integer
    end
  end
end

I’m trying to test this out by running my application in iex and doing the following:

alias Chordwitch.Chords.Chord
Chordwitch.Chords.Chord
iex(2)> alias Chordwitch.Progressions.Progression
Chordwitch.Progressions.Progression
iex(3)> alias Chordwitch.Repo
Chordwitch.Repo
iex(4)> p = %Progression{name: "iex Rock"}
%Chordwitch.Progressions.Progression{
  __meta__: #Ecto.Schema.Metadata<:built, "progressions">,
  id: nil,
  name: "iex Rock",
  tags: nil,
  chords: #Ecto.Association.NotLoaded<association :chords is not loaded>,
  inserted_at: nil,
  updated_at: nil
}
iex(5)> p = Repo.insert!(p)

Problem

When I run that last command and try and insert my progression (before building any associations), I get the following error:

** (UndefinedFunctionError) function Chords.Chord.__schema__/1 is undefined (module Chords.Chord is not available)
    Chords.Chord.__schema__(:primary_key)
    (ecto 3.10.1) lib/ecto/changeset/relation.ex:155: Ecto.Changeset.Relation.change/3
    (ecto 3.10.1) lib/ecto/changeset/relation.ex:526: anonymous fn/4 in Ecto.Changeset.Relation.surface_changes/3
    (elixir 1.14.4) lib/enum.ex:2468: Enum."-reduce/3-lists^foldl/2-0-"/3
    (ecto 3.10.1) lib/ecto/changeset/relation.ex:513: Ecto.Changeset.Relation.surface_changes/3
    (ecto 3.10.1) lib/ecto/repo/schema.ex:344: Ecto.Repo.Schema.do_insert/4
    (ecto 3.10.1) lib/ecto/repo/schema.ex:273: Ecto.Repo.Schema.insert!/4

This error is the reason I have alias ed Chord in the Progression module and vice versa, it’s not something that was in the guide.

Sorry for the long post but any pointers would be appreciated.

1 Like

Welcome!

New users need to have their posts validated by a moderator as an anti spam measure.

defmodule Chordwitch.Progressions.Progression do
  use Ecto.Schema
  import Ecto.Changeset
  alias Chords.Chord

This is your bug. You have done alias Chords.Chord and therefore this line here many_to_many :chords, Chord aliases to many_to_many :chords, Chords.Chord, but the module is Chordwitch.Chords.Chord

You should have alias Chordwitch.Chords.Chord instead, or just don’t alias and use the full module name.

1 Like

D’oh! Yes of course that was it, and I should have known as I’d aliased correctly in iex! But thanks very much for the quick reply to a confused newbie :slight_smile:

1 Like