0

I am using a has_many :through association between a Musician and an Instrument through a MusiciansInstrument join table:

class Musician < ActiveRecord::Base
  has_many :musicians_instruments
  has_many :instruments, through: :musicians_instruments
end

class Instrument < ActiveRecord::Base
  has_many :musicians_instruments
  has_many :musicians, through: :musicians_instruments
  validates :instrument_name, presence: true
end

class MusiciansInstrument < ActiveRecord::Base
  belongs_to :musician
  belongs_to :instrument 
  validate :began_playing, presence: true
end

I want to make sure that I know how many years each Musician has been playing each Instrument, so I have a required began_playing datetime field in my MusiciansInstrument table.

I would like to do this without accepts_nested_attributes_for if possible.

My schema.rb(minus timestamps):

create_table "musicians_instruments", force: true do |t|
  t.integer  "musician_id"
  t.integer  "instrument_id"
  t.datetime "began_playing"
end

create_table "instruments", force: true do |t|
  t.string   "instrument_name"
end

create_table "musicians", force: true do |t|
end

My question is what is how to best create objects using this association when making seeds and querying objects in the console. I also need to get FactoryGirl making these, but first things first.

1) In the console (using the approach proposed in this Stack Overflow)

musician = Musician.new 
#=> #<Musician id: nil, user_id: nil, created_at: nil, updated_at: nil> 
instrument = Instrument.new(:instrument_name => 'Bass Guitar') 
#=> #<Instrument id: nil, created_at: nil, updated_at: nil, instrument_name: "Bass Guitar"> 
instrument.save!
musician.musicians_instruments << MusiciansInstrument.create(:instrument => instrument, :began_playing => Time.now - 10.years)
#=> #<ActiveRecord::Associations::CollectionProxy [#<MusiciansInstrument id: 19, musician_id: nil, instrument_id: 15, began_playing: "2004-02-25 23:52:12">]>
musician.save! 
#=> ActiveRecord::RecordInvalid: Validation failed: Instruments can't be blank

2) In seeds.rb:

m7 = Musician.create
i7 = Instrument.create(:instrument_name => 'Voice')
m7.musicians_instruments << MusiciansInstrument.create(:instrument => i7, :began_playing => Time.now - 10.years)

These console queries return:

Musician.includes(:instruments).where(instruments.instrument_name => "Voice") #=>NameError: undefined local variable or method `instruments' for main:Object

Musician.joins(:instruments).where(instruments.instrument_name => "Voice") #=>NameError: undefined local variable or method `instruments' for main:Object

The seeds.rb statements work, and do create the associated objects and populate the through table with a began_playing date, but the queries are wrong. Two ways to query the began_playing field on the has_many :through join table are:

MusiciansInstrument.joins(:musician).where.not(began_playing: nil) 
#=> #<ActiveRecord::Relation [#<MusiciansInstrument id: 13, musician_id: 7, instrument_id: 7, began_playing: "1984-02-26 03:59:01">]> 

MusiciansInstrument.joins(:instrument).where.not(began_playing: nil) 
#=> #<ActiveRecord::Relation [#<MusiciansInstrument id: 13, musician_id: 7, instrument_id: 7, created_at: "2014-02-26 03:59:01", updated_at: "2014-02-26 03:59:01", began_playing: "1984-02-26 03:59:01">]>

Does anyone know how or if this could be queried from the Musician or Instrument objects? Something like this, which doesn't work:

Musician.joins(:musicians_instruments).where.not(:musicians_instruments.began_playing => nil)
Community
  • 1
  • 1
  • Man, this is ONLY timestamps :P – Ruby Racer Feb 26 '14 at 00:48
  • In your 1), you don't yet have a musician ID and you get an exception with musician.save!. You must save musician before create the association. – Ruby Racer Feb 26 '14 at 00:51
  • Thanks, beginner's mistake. I had been using has_and_belongs_to_many with the validation constraint on the Musician requiring an Instrument, and had gotten used to not being able to create a Musician without first giving it an Instrument. When I switched to has_many :through that became a fatal habit. That answers #1, and if you post your comment as an answer, I will mark it up. I still get the same error with #2, even using create to make a Musician, but my query is probably wrong, so I can't tell if my seeds.rb made a good association or not. – おすぎます Feb 26 '14 at 01:00
  • That's ok. In number 2, it's the same idea. – Ruby Racer Feb 26 '14 at 01:04
  • Actually, all the `MusiciansInstrument` instances created by `seeds.rb` look like this, `#`. All have nil for `began_playing`. So #2 is still in the wind.... Plus the queries about still throw `NameError`. – おすぎます Feb 26 '14 at 01:13
  • Maybe your syntax should be more like Musician.joins(:instruments).where(instruments: {instrument_name: "Voice"}) – Ruby Racer Feb 26 '14 at 01:23
  • Thanks, that works and adds to my understanding of how to query joins! – おすぎます Feb 27 '14 at 23:05

0 Answers0