# ActiveRecord puzzler for smart kids (preserving fk inserts when using has\_many :through, :condition

**URL:** <https://www.ruby-forum.com/t/activerecord-puzzler-for-smart-kids-preserving-fk-inserts-when-using-has-many-through-condition/142717>\
**Category:** Rails\
**Created:** [July 10, 2008, 8:22pm UTC](https://www.ruby-forum.com/t/activerecord-puzzler-for-smart-kids-preserving-fk-inserts-when-using-has-many-through-condition/142717 "2008-07-10T20:22:36Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![tshim](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c4cdca/32.png) [@tshim](https://www.ruby-forum.com/u/tshim)\
**Post date:** [July 10, 2008, 8:22pm UTC](https://www.ruby-forum.com/t/activerecord-puzzler-for-smart-kids-preserving-fk-inserts-when-using-has-many-through-condition/142717/1 "2008-07-10T20:22:36Z")

</div>

You kids and your fancy ActiveRecord stuff. You’re having all the fun  
while us geezers are writing complex JOINS by hand. Whee!

Wondering if anyone has reached a more elegant solution to this sort  
of has\_many :through association.

Say I’ve got Jobs, Participations, Roles and Users.

A Job has\_many Users through Participations.

A Participation belongs\_to a Role, User, and Job.

One type of role is a “Candidate”, represented as a Role object  
Role::CANDIDATE.

To create a custom AR collection like job.candidates, I’ve implemented  
the following:  
(sorry, pastie is broken right now)

class Job  
has\_many :participations  
has\_many :users, :through =\> :participations  
has\_many :candidates, :through =\> :participations,  
:source =\> :user,  
:conditions =\> [“role\_id = ?”,  
Role::CANDIDATE.id]  
end

So job.candidates yields:  
SELECT “users”.\* FROM “users” INNER JOIN participations ON users.id =  
participations.user\_id WHERE ((“participations”.job\_id = 28) AND  
((role\_id = 6)))

And job.candidates \<\< my\_user yields:  
INSERT INTO “participations” (“job\_id”, “updated\_at”, “role\_id”,  
“user\_id”, “created\_at”) VALUES(28, ‘2008-07-10 17:52:55.031711’,  
NULL, 11, ‘2008-07-10 17:52:55.031711’)

It would be great if AR would, for through relationships with  
conditions, automatically take the condition clause and be smart  
enough to intelligently supply the id value during the INSERT. In  
other words, I need that NULL value to be ‘6’.

My first approach to this problem was to try using a :before\_add  
function, like so:

:before\_add =\> Proc.new {|job, user| #would love to do it here}

But :before\_add only receives a reference to the local and foreign  
object, eg the Job and the User. It would be great if somehow I could  
get a reference to the object in the join (Participation) such that:

:before\_add =\> Proc.new {|job, participation, user| participation.role  
= Role::CANDIDATE}

I realize I can create a method add\_candidate(user) and manually do  
this stuff… but has anyone encountered a similar situation and  
reached an elegant solution?

Thanks for taking the time to read this.

---

<div class="post-metadata">

**Author:** ![tshim](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c4cdca/32.png) [@tshim](https://www.ruby-forum.com/u/tshim)\
**Post date:** [July 10, 2008, 8:53pm UTC](https://www.ruby-forum.com/t/activerecord-puzzler-for-smart-kids-preserving-fk-inserts-when-using-has-many-through-condition/142717/2 "2008-07-10T20:53:07Z")

</div>

Hi –

On Thu, 10 Jul 2008, tshim wrote:

> has\_many :participations  
> ((role\_id = 6)))
> 
> And job.candidates \<\< my\_user yields:  
> INSERT INTO “participations” (“job\_id”, “updated\_at”, “role\_id”,  
> “user\_id”, “created\_at”) VALUES(28, ‘2008-07-10 17:52:55.031711’,  
> NULL, 11, ‘2008-07-10 17:52:55.031711’)
> 
> It would be great if AR would, for through relationships with  
> conditions, automatically take the condition clause and be smart  
> enough to intelligently supply the id value during the INSERT. In  
> other words, I need that NULL value to be ‘6’.

I don’t think the conditions can be reverse-engineered in the general  
case, though. It might be [“role\_id in (?)”, [bunch,of,values]], for  
example.

> = Role::CANDIDATE}
> 
> I realize I can create a method add\_candidate(user) and manually do  
> this stuff… but has anyone encountered a similar situation and  
> reached an elegant solution?

Try this:

has\_many :candidates,  
:through =\> :participations,  
:source =\> :user,  
:conditions =\> [“role\_id = ?”, Role::CANDIDATE] do  
def \<\<(user)  
Participation.create(:user\_id =\> user.id,  
:role\_id =\> Role::CANDIDATE,  
:job\_id =\> proxy\_owner.id)  
end  
end

It’s the (very cool) technique of adding custom methods to association  
collections. This one is inspired by Josh S.'s blog post on this  
problem (customizing \<\<).[1]

David

[1] [has\_many :through - New on edge: Magic join model creation](http://blog.hasmanythrough.com/2006/8/19/magic-join-model-creation)

–  
Rails training from David A. Black and Ruby Power and Light:  
Intro to Ruby on Rails July 21-24 Edison, NJ  
Advancing With Rails August 18-21 Edison, NJ  
See [http://www.rubypal.com](http://www.rubypal.com) for details and updates!

---

<div class="post-metadata">

**Author:** ![tshim](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/c4cdca/32.png) [@tshim](https://www.ruby-forum.com/u/tshim)\
**Post date:** [July 11, 2008, 6:23pm UTC](https://www.ruby-forum.com/t/activerecord-puzzler-for-smart-kids-preserving-fk-inserts-when-using-has-many-through-condition/142717/3 "2008-07-11T18:23:29Z")

</div>

Hi David, thanks for contributing to the group and for taking the time  
to help me specifically.

That’s exactly the behavior I was trying to implement. I’d seen a  
similar post on Josh S.'s hasmanythrough blog… but not the one  
you pointed out.

You get a gold star!

Readers of this thread should totally explore this! It’s a transparent  
but powerful approach to controlling your AR associations.
