views:

603

answers:

3

I have three models:

class ReleaseItem < ActiveRecord::Base
  has_many :pack_release_items
  has_one :pack, :through => :pack_release_items
end

class Pack < ActiveRecord::Base
  has_many :pack_release_items
  has_many :release_items, :through=>:pack_release_items
end

class PackReleaseItem < ActiveRecord::Base
  belongs_to :pack
  belongs_to :release_item
end

The problem is that, during execution, if I add a pack to a release_item it is not aware that the pack is a pack. For instance:

Loading development environment (Rails 2.1.0)
>> item = ReleaseItem.new(:filename=>'MAESTRO.TXT')
=> #<ReleaseItem id: nil, filename: "MAESTRO.TXT", created_by: nil, title: nil, sauce_author: nil, sauce_group: nil, sauce_comment: nil, filedate: nil, filesize: nil, created_at: nil, updated_at: nil, content: nil>
>> pack = Pack.new(:filename=>'legion01.zip', :year=>1998)
=> #<Pack id: nil, filename: "legion01.zip", created_by: nil, filesize: nil, items: nil, year: 1998, month: nil, filedate: nil, created_at: nil, updated_at: nil>
>> item.pack = pack
=> #<Pack id: nil, filename: "legion01.zip", created_by: nil, filesize: nil, items: nil, year: 1998, month: nil, filedate: nil, created_at: nil, updated_at: nil>
>> item.pack.filename
NoMethodError: undefined method `filename' for #<Class:0x2196318>
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:1667:in `method_missing_without_paginate'
    from /usr/local/lib/ruby/gems/1.8/gems/mislav-will_paginate-2.3.3/lib/will_paginate/finder.rb:164:in `method_missing'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_collection.rb:285:in `send'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_collection.rb:285:in `method_missing_without_paginate'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/base.rb:1852:in `with_scope'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_proxy.rb:168:in `send'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_proxy.rb:168:in `with_scope'
    from /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.1.0/lib/active_record/associations/association_collection.rb:281:in `method_missing_without_paginate'
    from /usr/local/lib/ruby/gems/1.8/gems/mislav-will_paginate-2.3.3/lib/will_paginate/finder.rb:164:in `method_missing'
    from (irb):5
>>

It seems that I should have access to item.pack, but it is unaware that the pack is a Pack item.

+6  A: 

It appears that your usage of has_one :through is correct. The problem you're seeing has to do with saving objects. For an association to work, the object that is being referenced needs to have an id to populate the model_id field for the object. In this case, PackReleaseItems have a pack_id and a release_item_id field that need to be filled for the association to work correctly. Try saving before accessing objects through an association.

Misplaced
But if I use has_many :through, belongs_to, or has_one, I don't have to save first.
lordscarlet
Yep, that seems to have taken care of it. I don't think I have to do that with other associations, though.
lordscarlet
Does that take care of it? Can you post a clip of your interactive console session? I made a little test application using Rails 2.1.0 and even saving beforehand did not help the situation, which is why I submitted my answer.
Ian Terrell
That did. I will post the output tonight or tomorrow morning (I do .NET dev at work).
lordscarlet
+2  A: 

Your problem is in how you're associating the ReleaseItem and the Pack.

has_many :through and has_one :through both work through an object that also acts as a join table, in this case PackReleaseItem. Since this is not just a join table (if it were, you should just use has_many without :through), properly creating the association requires creating the join object, like so:

>> item.pack_release_items.create :pack => pack

What you're doing with your item.pack = pack call is simply associating the objects in memory. When you go to look it up again, it looks "through" the pack_release_items, which is empty.

Ian Terrell
>> item.pack_release_items.create :pack => pack=> #<PackReleaseItem id: 19, pack_id: nil, release_item_id: 5, created_at: "2008-09-18 11:33:31", updated_at: "2008-09-18 11:33:31">>> item.pack=> nil
lordscarlet
That may have occurred because the pack did not satisfy all of the validators. I will try it again when I get home.
lordscarlet
Pack is nil above in your example because the Pack hasn't been saved -- you can see "pack_id: nil" in your PackReleaseItem.
Ian Terrell
+1  A: 

You want to save or create (instead of new) the item and pack. Otherwise, the database has not assigned id's for the association.

Thanatos