Search code examples
ruby-on-railsnested-attributes

how to update schema after adding "accepts_nested_attributes_for" to a model


here is my model

class Lineup < ApplicationRecord
  has_many :artists
  accepts_nested_attributes_for :artists
  belongs_to :event
end

class Artist < ApplicationRecord
  has_many :events, :through => :lineups
  has_many :lineups
end

when running this in the console

Lineup.new(artists_attributes: [{artist_id: 1}, {artist_id: 2}])

the error is ActiveModel::UnknownAttributeError: unknown attribute 'artists_attributes' for Lineup. Obviously, I can't just drop something like that into a model and expect any changes to be from that alone. Do I need to run a migration for this? If so, what needs to be in it?

schema:

  create_table "artists", force: :cascade do |t|
    t.string   "name"
    t.text     "bio"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "lineup_id"
    t.integer  "event_id"
  end

  add_index "artists", ["event_id"], name: "index_artists_on_event_id", using: :btree
  add_index "artists", ["lineup_id"], name: "index_artists_on_lineup_id", using: :btree

create_table "lineups", force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer  "artist_id"
    t.integer  "event_id"
  end

  add_index "lineups", ["artist_id"], name: "index_lineups_on_artist_id", using: :btree
  add_index "lineups", ["event_id"], name: "index_lineups_on_event_id", using: :btree

Solution

  • I would set it up like this

    schema

    table Lineup
      ...
    
    table Artist
      ...
    
    table LineupArtists
      lineup_id: Integer
      artist_id: Integer
    

    models

    class Artist < ApplicationRecord
      has_many :lineup_artists, dependent: :destroy
      has_many :lineups, through: :lineup_artists
    end
    
    class LineupArtist < ApplicationRecord
       belongs_to :lineup
       belongs_to :artist
    
       accepts_nested_attributes_for :lineup
    end 
    
    class Lineup < ApplicationRecord
      has_many :lineup_artists, inverse_of: :lineup, dependent: :destroy
      has_many :artists, through: lineup_artists
    
      accepts_nested_attributes_for :lineup_artists
    end
    

    The way you currently have it (with Artists having a lineup_id and Lineups having an artist_id) each model can only have one of the other (i.e. an Artist can have one lineup and vise versa). A join table gives the ability to have a many-to-many relationship.

    Setting up nested attributes is a little trickier with a many-to-many but I believe what I posted should cover it. If not got some of the weirder bits from this post (https://robots.thoughtbot.com/accepts-nested-attributes-for-with-has-many-through). Hope this helps.