Accessing join model attributes in has_many :through

After scouring these forums, the blogs, & wiki’s I’ve finally decided to
ask the question because either nobody has encountered this problem or,
more likely, I I’m missing something obvious.

I am attempting to access the extra attributes on the join model through
a has_many :through association. Most code examples I have found don’t
deal with this situation even though, I think, that was part of decision
to deprecate HABTM in favor of HMT.

I have tables “accounts”, “links”, and the join table “account_links”.

In the end, I want to be able to do this:

a = Account.find(1)
a.links[0].label
=> “untitled”

The closest I’ve been able to get is:

a = Account.find(1)
a.links[0].account_links[0].label
=> “untitled”

The closest solution I found was at
Join table attributes piggy backed with has_many through - Rails - Ruby-Forum but I could not get it to work.
The poster never replied back if the solution worked for them.

-TABLES-

create_table :accounts do |t|
t.column :email_address, :string, :limit => 40, :null => false
t.column :password, :string, :limit => 40, :null => false
t.column :created_at, :datetime, :null => false
end

create_table :links do |t|
t.column :url, :text, :null => false
t.column :created_at, :datetime
end

create_table :account_links do |t|
t.column :account_id, :integer, :null => false
t.column :link_id, :integer, :null => false
t.column :label, :string, :limit => 20, :null => true
end

-MODELS-

class Account < ActiveRecord::Base
has_many :account_links
has_many :links, :through => :account_links
end

class Link < ActiveRecord::Base
has_many :account_links
has_many :account, :through => :account_links
end

class AccountLink < ActiveRecord::Base
before_create :set_label

belongs_to :account
belongs_to :link

def set_label
write_attribute(“label”, ‘untitled’)
end

end

Thanks for any guidance.

Hi –

On Wed, 15 Aug 2007, Sean O’grady wrote:

I have tables “accounts”, “links”, and the join table “account_links”.

In the end, I want to be able to do this:

a = Account.find(1)
a.links[0].label
=> “untitled”

You’re calling “label” on a Link object, but links don’t have a label
field or attribute. So somewhere along the way you have to get hold of
the AccountLink object you want.

class Link < ActiveRecord::Base
has_many :account_links
has_many :account, :through => :account_links

That should be :accounts (plural), though I don’t think it has a
direct bearing on the question you’re asking.

David

Hi –

On Wed, 15 Aug 2007, Sean O’grady wrote:

You’re calling “label” on a Link object, but links don’t have a label
field or attribute. So somewhere along the way you have to get hold of
the AccountLink object you want.

I guess what I was hoping for was that by association it would tag on
the extra attributes from the AccountLink object to the Link object.
That way, I could loop through the links collection and grab both
“label” and “url” at the same time.

There are a couple of problems with that. First, what if Link objects
also had a label field? Second, you can in theory have more than one
AccountLink between a given Account and a given Link. You can use the
:uniq flag to make this not happen, but it’s still kind of contrary to
the design overall to have unknown methods on an Account object
automatically delegated to an AccountLink object.

David

You’re calling “label” on a Link object, but links don’t have a label
field or attribute. So somewhere along the way you have to get hold of
the AccountLink object you want.

I guess what I was hoping for was that by association it would tag on
the extra attributes from the AccountLink object to the Link object.
That way, I could loop through the links collection and grab both
“label” and “url” at the same time.

class Link < ActiveRecord::Base
has_many :account_links
has_many :account, :through => :account_links

That should be :accounts (plural), though I don’t think it has a
direct bearing on the question you’re asking.

Thanks, that would have been a problem later.

There are a couple of problems with that. First, what if Link objects
also had a label field? Second, you can in theory have more than one
AccountLink between a given Account and a given Link. You can use the
:uniq flag to make this not happen, but it’s still kind of contrary to
the design overall to have unknown methods on an Account object
automatically delegated to an AccountLink object.

All very true. I guess I was blindly expecting more Rails magic. :slight_smile:

I’ve come up with two solutions for myself that I haven’t fully tried
yet:

  1. Rename “account_links” to “links” and do away “has_many :through”
    stuff while keeping the other has_many’s. This should yield the ability
    to do things like account.links[0].label & account.links[0].link.url.
    Not as elegant as magic, but perhaps less confusing than having two
    different collections (.links & .account_links) on the model.

  2. Modify the Link object with an extra method for “label” that simply
    sends back self.account_link.label. This gets me to where I wanted to be
    in the first place and if I make private the one association/collection
    I don’t want to access (account_links) I should find nirvana.

Thanks for your help David.