Forum: Ruby on Rails has_many :through and foreign key parameters

Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 18:14
I just took my HABTM and turned it into a :through since my join table
has another "non-joiny" attribute.

I went from this:

has_many_and_belongs_to :jobs,  :join_table => 'tablename',
                   :foreign_key => 'x',
                   :association_foreign_key => 'y'

to this:

has_many :jobs,  :through => 'model_name'

Should this work?  I'm thinking that the foreign_key and assoc. foreign
key are no longer necessary since Rails can just go to the join model
and figure out the foreign keys, right?

Wes
9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2006-07-10 18:36
Wes Gamble wrote:
> I just took my HABTM and turned it into a :through since my join table
> has another "non-joiny" attribute.
>
> I went from this:
>
> has_many_and_belongs_to :jobs,  :join_table => 'tablename',
>                    :foreign_key => 'x',
>                    :association_foreign_key => 'y'
>
> to this:
>
> has_many :jobs,  :through => 'model_name'
>
> Should this work?  I'm thinking that the foreign_key and assoc. foreign
> key are no longer necessary since Rails can just go to the join model
> and figure out the foreign keys, right?

That won't work - you need to go through an association as a symbol,
(e.g. :join_models), not a name as a string. If you're just starting out
with has_many :through, I've got some good examples on my blog. You
should start with
http://blog.hasmanythrough.com/articles/2006/04/20...

You can use :foreign_key for hmt, but instead of
:association_foreign_key you'll use the :source option to select the
join model's association to join with the third table.

--
Josh Susser
http://blog.hasmanythrough.com
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 18:44
Josh Susser wrote:
> Wes Gamble wrote:
>> I just took my HABTM and turned it into a :through since my join table
>> has another "non-joiny" attribute.
>>
>> I went from this:
>>
>> has_many_and_belongs_to :jobs,  :join_table => 'tablename',
>>                    :foreign_key => 'x',
>>                    :association_foreign_key => 'y'
>>
>> to this:
>>
>> has_many :jobs,  :through => 'model_name'
>>
>> Should this work?  I'm thinking that the foreign_key and assoc. foreign
>> key are no longer necessary since Rails can just go to the join model
>> and figure out the foreign keys, right?
>
> That won't work - you need to go through an association as a symbol,
> (e.g. :join_models), not a name as a string. If you're just starting out
> with has_many :through, I've got some good examples on my blog. You
> should start with
> http://blog.hasmanythrough.com/articles/2006/04/20...
>
> You can use :foreign_key for hmt, but instead of
> :association_foreign_key you'll use the :source option to select the
> join model's association to join with the third table.
>
> --
> Josh Susser
> http://blog.hasmanythrough.com

Johs,

Just discovered your blog this AM and am really digging it.  Thanks so
much for doing it.

Please indulge me in a couple of questions, if you would.

1) The API docs. for ActiveRecord::Base say that :foreign_key will not
be evaluated if you use has_many :through - are the API docs. wrong?

2) The API docs. on 'source' are really confusing:

":source: Specifies the source association name used by has_many
:through queries. Only use it if the name cannot be inferred from the
association. has_many :subscribers, :through => :subscriptions will look
for either +:subscribers+ or +:subscriber+ on Subscription, unless a
+:source+ is given."

I would think that :source has nothing to do with foreign_key column
names?  That source is a reference to an alternate model name, if, for
some reason you had a different collection that you wanted to reference
in the relationship.

Thanks,
Wes
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 19:15
> 2) The API docs. on 'source' are really confusing:
>
> ":source: Specifies the source association name used by has_many
> :through queries. Only use it if the name cannot be inferred from the
> association. has_many :subscribers, :through => :subscriptions will look
> for either +:subscribers+ or +:subscriber+ on Subscription, unless a
> +:source+ is given."
>
> I would think that :source has nothing to do with foreign_key column
> names?  That source is a reference to an alternate model name, if, for
> some reason you had a different collection that you wanted to reference
> in the relationship.
>
> Thanks,
> Wes

I keep re-reading it, but I have no idea what that :source option
documentation is trying to say.  Am I supposed to be specifying a
foreign_key _column name_, and then a source _model object name_ to get
through to the other side?

WG
9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2006-07-10 19:31
Wes Gamble wrote:
> Just discovered your blog this AM and am really digging it.  Thanks so
> much for doing it.

Thanks, and you're welcome.

> Please indulge me in a couple of questions, if you would.
>
> 1) The API docs. for ActiveRecord::Base say that :foreign_key will not
> be evaluated if you use has_many :through - are the API docs. wrong?

Oh, sorry.  You'd use :foreign_key on the has_many for the join model.
That sets up the relationship of your main table to the join model.
:source is then used on the :through instead of :association_foreign_key

> 2) The API docs. on 'source' are really confusing:

Yeah, I should try to clean those docs up. They are rather opaque. I
don't really like the term :source either.

:source is used to specify the association in the join model to get to
the other side. You can see examples in
http://blog.hasmanythrough.com/articles/2006/05/06...
http://blog.hasmanythrough.com/articles/2006/04/21...

--
Josh Susser
http://blog.hasmanythrough.com
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 21:08
So just to be explicit, from class A (backed by database table Table_A),
attempting to relate to class B, (backed by database table Table_B),
joined by model C

has_many :whatevers, :through => 'C',
:foreign_key => 'key into Table_A', :source => 'B'

?
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 21:10
So just to be explicit, from class A (backed by database table Table_A),
attempting to relate to class B, (backed by database table Table_B),
joined by model C

has_many :whatevers, :through => 'C',
         :foreign_key => 'key into Table_A', :source => 'B'

?

So there is _no_ reference to the foreign key column _from_ B into C
(unlike the HABTM specification), correct?

Thanks,
Wes
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 21:10
So just to be explicit, from class A (backed by database table Table_A),
attempting to relate to class B, (backed by database table Table_B),
joined by model C

has_many :Bs, :through => 'C',
         :foreign_key => 'key into Table_A', :source => 'B'

?

So there is _no_ reference to the foreign key column _from_ B into C
(unlike the HABTM specification), correct?

Thanks,
Wes
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 21:25
Wes Gamble wrote:
> So just to be explicit, from class A (backed by database table Table_A),
> attempting to relate to class B, (backed by database table Table_B),
> joined by model C
>
> has_many :Bs, :through => 'C',
>          :foreign_key => 'key into Table_A', :source => 'B'
>
> ?
>
> So there is _no_ reference to the foreign key column _from_ B into C
> (unlike the HABTM specification), correct?
>
> Thanks,
> Wes

Better yet, let's be really explicit.

I am trying to relate Job to TargetList through JobListAssociation.
None of the tables have "traditional" Rails naming conventions except
for the join table which happens to have an "id" column.

job.rb:

class Job < ActiveRecord::Base
  set_table_name  :JobData
  set_primary_key :JobReferenceNumber

  has_many :target_lists, :through => :job_list_association,
                          :foreign_key => :JobReferenceNumber,
                          :source => :target_list

---
target_list.rb:

class TargetList < ActiveRecord::Base
  set_table_name  :DataSetInfo
  set_primary_key :DataSetID

  has_many :jobs, :through => :job_list_association,
                  :foreign_key => :DataSetID,
                  :source => :job

---
job_list_association.rb:

class JobListAssociation < ActiveRecord::Base
  set_table_name :DataTables

  belongs_to :job, :foreign_key => 'JobReferenceNumber'
  belongs_to :target_list, :foreign_key => 'DataSetID'

When I attempt to call @current_job.target_lists.include? in my view, I
see:

 ActiveRecord::HasManyThroughAssociationNotFoundError in
E_simply#job_params

C:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/reflection.rb:169:in
`check_validity!'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/associations/has_many_through_association.rb:6:in
`initialize'
C:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.14.3/lib/active_record/associations.rb:876:in
`target_lists'

Do I need to add a :condition onto my has_many specification?

Should I always be using symbols instead of strings as hash values in
these ActiveRecord relationship method calls?  If so, why?

Thanks,
Wes
9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2006-07-10 21:30
Wes Gamble wrote:
> So just to be explicit, from class A (backed by database table Table_A),
> attempting to relate to class B, (backed by database table Table_B),
> joined by model C
>
> has_many :whatevers, :through => 'C',
>          :foreign_key => 'key into Table_A', :source => 'B'
>
> So there is _no_ reference to the foreign key column _from_ B into C
> (unlike the HABTM specification), correct?

You always need 2 associations in each class to do a join model - 2
belongs_to in the join model, then a has_many and a has_many :through in
the joined classes.

has_many :joinings, :foreign_key = "fk_id"
has_many :things, :through => :joinings, :source => :other

Please look at the examples on my blog. I don't want to repeat myself
here.

--
Josh Susser
http://blog.hasmanythrough.com
Bb4bdf2b184027bc38d4fb529770cde5?d=identicon&s=25 Wes Gamble (weyus)
on 2006-07-10 21:44
Josh Susser wrote:
> Wes Gamble wrote:
>> So just to be explicit, from class A (backed by database table Table_A),
>> attempting to relate to class B, (backed by database table Table_B),
>> joined by model C
>>
>> has_many :whatevers, :through => 'C',
>>          :foreign_key => 'key into Table_A', :source => 'B'
>>
>> So there is _no_ reference to the foreign key column _from_ B into C
>> (unlike the HABTM specification), correct?
>
> You always need 2 associations in each class to do a join model - 2
> belongs_to in the join model, then a has_many and a has_many :through in
> the joined classes.
>
> has_many :joinings, :foreign_key = "fk_id"
> has_many :things, :through => :joinings, :source => :other
>
> Please look at the examples on my blog. I don't want to repeat myself
> here.
>
> --
> Josh Susser
> http://blog.hasmanythrough.com

Josh,

Thank you.  That was a major piece of information that I didn't have
before. And, believe it or not, I read two of your blog entries
(dance-off and one other) end to end :) before I ever posted.

Does using symbols vs. strings really matter when setting the values of
the hash parameters on the way into these methods?  Is :through =>
:join_table going to work differently than :through => 'join_table'?

I have some questions about whether this approach buys you much over the
HABTM approach which I will pose in a separate thread.

Thanks again,
Wes
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.