tshim
2008-Jul-10 18:22 UTC
ActiveRecord puzzler for smart kids (preserving fk inserts when using has_many :through, :conditions, :before_add)
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. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
David A. Black
2008-Jul-10 18:52 UTC
Re: ActiveRecord puzzler for smart kids (preserving fk inserts when using has_many :through, :conditions, :before_add)
Hi -- On Thu, 10 Jul 2008, tshim wrote:> > 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''.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.> 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?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 Susser''s blog post on this problem (customizing <<).[1] David [1] 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 for details and updates! --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
tshim
2008-Jul-11 16:22 UTC
Re: ActiveRecord puzzler for smart kids (preserving fk inserts when using has_many :through, :conditions, :before_add)
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 Susser''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. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---