Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does has_one :through work?

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.

like image 574
Doug Moore Avatar asked Sep 18 '08 04:09

Doug Moore


2 Answers

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.

like image 69
Misplaced Avatar answered Sep 20 '22 11:09

Misplaced


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.

like image 30
Ian Terrell Avatar answered Sep 20 '22 11:09

Ian Terrell