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)