rubyruby-on-rails-5audio-playerplaylistaudiotrack

How do I call a join table in my show method? UPDATED


[UPDATE, Scroll down for solution]

I am busy developing a music streaming application with Rails and am stuck on a basic problem. I am not sure how to call my joint table.

This is my flow:

I have 3 tables that relate to playlists and tracks

create_table "tracks", force: :cascade do |t|
    t.string "title"
    t.text "description"
    t.string "artist"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "album_id", null: false
    t.bigint "user_id", null: false
    t.string "photo"
    t.string "track"
    t.index ["album_id"], name: "index_tracks_on_album_id"
    t.index ["user_id"], name: "index_tracks_on_user_id"
  end

create_table "playlists", force: :cascade do |t|
    t.string "playlist_title"
    t.text "playlist_description"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "user_id", null: false
    t.index ["user_id"], name: "index_playlists_on_user_id"
  end

create_table "playlists_tracks", id: false, force: :cascade do |t|
    t.bigint "playlist_id", null: false
    t.bigint "track_id", null: false
  end

Desired outcome -> Render tracks in Show.html.erb where tracks have a playlist_id of x

My current code looks like this in my playlists_controller.rb

def show
    @playlist = Playlist.find(params[:id])
    @track = Track.where(playlist_id: params[:id])
end

the result is an error saying

column tracks.playlist_id does not exist
LINE 1: SELECT "tracks".* FROM "tracks" WHERE "tracks"."playlist_id"...

If i go to my tracks table in schema, it makes sense that it cannot find a playlist_id value because the table doesn't have a playlist_id column.

However, there is a playlist_id in my playlists_tracks joint table along with a track_id.

What I don't understand >>

When a user adds a track, I know the track info is being added to the tracks table. But there is no info that is added to playlists_tracks table. I am expecting that when a user adds a track to a playlist, this is when an existing track will be associated to a/many playlist(s). I have not yet created the function that will add a track to the playlist.

Right now I am merely trying view the SHOW page of a specific playlist which contains code that will render the related tracks. My current code does not find the playlist_id in tracks table because it does not exist. My first thought is to change my SHOW method so that it searches the playlists_tracks table instead of the tracks table.

However, my syntax does not make sense. do I call my playlists_tracks table like below? With a capital P and singular track?

New show method:

def show
        @playlist = Playlist.find(params[:id])
        @playlist_track = Playlists_track.where(playlist_id: params[:id])
    end

Hope the size of my description helps and is not an overkill! Thanks for your time.

For further reference, here are my associated models

class Playlist < ApplicationRecord
    has_many :tracks, through: :playlists_tracks
    has_many :tags, through: :playlists_tags
    belongs_to :user

    validates :playlist_title, presence: true, length: { in: 1..20 }
    validates :playlist_description, presence: true, length: { in: 10..60 }

    has_one_attached :photo
end

class Track < ApplicationRecord
    belongs_to :album
    belongs_to :user
    has_many :playlists, through: :playlists_tracks
    has_many :tags, through: :tags_tracks

    validates :title, presence: true, length: { in: 1..20 }
    validates :description, presence: true, length: { in: 10..60 }
    has_one_attached :photo
    has_one_attached :track
end

[UPDATE]

  1. I was missing a PlaylistTrack Model
  2. My has_many associations needed correcting
  3. Playlist controller show method changed syntax

new model

class PlaylistTrack < ApplicationRecord
belongs_to :track
belongs_to :playlist
end

Addition to Schema

  create_table "playlist_tracks", force: :cascade do |t|
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.bigint "playlist_id", null: false
    t.bigint "track_id", null: false
    t.index ["playlist_id"], name: "index_playlist_tracks_on_playlist_id"
    t.index ["track_id"], name: "index_playlist_tracks_on_track_id"
  end

corrected Playlist Model

class Playlist < ApplicationRecord
    has_many :playlist_tracks
    has_many :tracks, through: :playlist_tracks
    has_many :tags, through: :playlist_tags
    belongs_to :user

    validates :playlist_title, presence: true, length: { in: 1..20 }
    validates :playlist_description, presence: true, length: { in: 10..60 }

    has_one_attached :photo
end

Corrected Track Model

class Track < ApplicationRecord
    belongs_to :album
    belongs_to :user
    has_many :playlist_tracks
    has_many :playlists, through: :playlist_tracks
    has_many :tags, through: :tags_tracks

    validates :title, presence: true, length: { in: 1..20 }
    validates :description, presence: true, length: { in: 10..60 }
    has_one_attached :photo
    has_one_attached :track
end

Corrected Show Method

def show
    @playlist = Playlist.find(params[:id])
    @tracks = @playlist.tracks
end

Join tables with N:N relationships are so confusing. I am anticipating further errors as my tests in console are not promising(seeding a playlist with a track) but at least the Playlist Show Page is now rendering.

Hope this helps and if you have any pointers, please leave them below :)


Solution

  • Creating as answer to include code snippets: I'm going to assume that the "playlist_tracks" table was created with foreign keys/references to the relevant tables it is joined to:

    class CreatePlaylistTracks < ActiveRecord::Migration[6.0]
      def change
        create_table :playlist_tracks do |t|
          t.references :playlist, null: false, foreign_key: true
          t.references :track, null: false, foreign_key: true
    
          t.timestamps
        end
      end
    end
    

    you'll need to tweak your model (also assuming that you have a PlaylistTrack model defined, with belongs_tos in it):

    class Playlist < ApplicationRecord
        has_many :playlist_tracks, inverse_of: :playlist
        has_many :tracks, through: :playlist_tracks
        has_many :tags, through: :playlist_tags
        belongs_to :user
    
        validates :playlist_title, presence: true, length: { in: 1..20 }
        validates :playlist_description, presence: true, length: { in: 10..60 }
    
        has_one_attached :photo
    end
    

    then in the controller, you should just be able to do:

    def show
        @playlist = Playlist.find(params[:id])
        @track = @playlist.tracks
    end
    

    can you see if that makes a difference?