Jump to content

The ultimate community for Ruby on Rails developers.


Photo

Is there an AR association to do this?


  • Please log in to reply
10 replies to this topic

#1 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 03:59 PM

I have an equipment model and a technician model:

class Device < ActiveRecord::Base
  attr_accessible :name, :technician_id, :backup_tech_id
  belongs_to :technician
end

class Technician < ActiveRecord::Base
  attr_accessible :name
  has_many :devices
end

Each Device has to have a primary technician and a backup technician. Obviously it wouldn't make sense to create a separate "BackupTech" model so my question is, is there some AR association voodoo that will let me use a Technician as a "BackupTech"?

 

Thanks.



#2 Jemagee

Jemagee

    Inspector

  • Members
  • 62 posts

Posted 15 April 2014 - 04:12 PM

I believe you can make multiple foreign keys to the same table - you just must be more explicit

 

in class Device

belongs_to :technician, class: "Technician", foreign_key: "technician_id"

belongs_to :backup_tech, class: "Technician", foreign_key: "backup_tech_id"

 

And the reverse in technician

 

As someone who comes from the database world - i would just create a cross join table with device_id, tech_id, backup_tech_id

 

Be sure to index your tech id columns for searching purposes



#3 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 04:17 PM

I believe you can make multiple foreign keys to the same table - you just must be more explicit

 

in class Device

belongs_to :technician, class: "Technician", foreign_key: "technician_id"

belongs_to :backup_tech, class: "Technician", foreign_key: "backup_tech_id"

 

And the reverse in technician

 

 

Yeah, I sort of sussed out that second belongs_to line in Device class, but can't figure out what the reverse would be in the Technician class. Would you be so kind? :)



#4 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 09:32 PM

Nevermind. I figured it out. Thanks Jemagee. 



#5 Jemagee

Jemagee

    Inspector

  • Members
  • 62 posts

Posted 15 April 2014 - 09:35 PM

Can you list it - I'm a beginner and was having trouble figuring out how to write out the reverse because I wasn't sure you could list

 

has_many :devices

 

twice



#6 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 09:46 PM

Can you list it - I'm a beginner and was having trouble figuring out how to write out the reverse because I wasn't sure you could list

 

has_many :devices

 

twice

class Device < ActiveRecord::Base
  attr_accessible :name, :technician_id, :backup_tech_id
  belongs_to :technician
  belongs_to :backup_tech, :class_name => 'Technician', :foreign_key => 'backup_tech_id'
end

class Technician < ActiveRecord::Base
  attr_accessible :name
  has_many :devices
  has_many :devices, :foreign_key => 'backup_tech_id'
end

With the following records in Device and Technician:

<Device id: 1, name: "Copier1", technician_id: 1, backup_tech_id: 2, created_at: "2014-04-15 15:18:23", updated_at: "2014-04-15 16:24:07">

<Technician id: 1, name: "Colin", created_at: "2014-04-15 15:10:51", updated_at: "2014-04-15 15:10:51">

<Technician id: 2, name: "Chris", created_at: "2014-04-15 15:10:57", updated_at: "2014-04-15 15:10:57">

I can do:

irb(main):012:0> Device.find(1).technician
=> #<Technician id: 1, name: "Colin", created_at: "2014-04-15 15:10:51", updated_at: "2014-04-15 15:10:51">
irb(main):013:0> Device.find(1).backup_tech
=> #<Technician id: 2, name: "Chris", created_at: "2014-04-15 15:10:57", updated_at: "2014-04-15 15:10:57">

Unfortunately, the reverse doesn't quite work. i.e.

irb(main):015:0> Technician.find(1).devices
=> []

irb(main):016:0> Technician.find(2).devices
=> [#<Device id: 1, name: "Copier1", technician_id: 1, backup_tech_id: 2, created_at: "2014-04-15 15:18:23", updated_at: "2014-04-15 16:24:07">]

So, I guess there's still something I'm missing.  :mellow:



#7 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 10:32 PM   Best Answer

OK. I think I have a workable solution to this now. With these two model definitions:

class Device < ActiveRecord::Base
  attr_accessible :name, :technician_id, :backup_tech_id
  belongs_to :technician
  belongs_to :backup_tech, :class_name => 'Technician', :foreign_key => 'backup_tech_id'
end

class Technician < ActiveRecord::Base
  attr_accessible :name
  has_many :primary_devices, :class_name => 'Device', :foreign_key => 'technician_id'
  has_many :backup_devices, :class_name => 'Device', :foreign_key => 'backup_tech_id'
end

I can now do the following:

irb(main):001:0> d = Device.find 1
=> #<Device id: 1, name: "Copier1", technician_id: 1, backup_tech_id: 2, created_at: "2014-04-15 15:18:23", updated_at: "2014-04-15 16:24:07">
irb(main):002:0> t1 = Technician.find 1
=> #<Technician id: 1, name: "Colin", created_at: "2014-04-15 15:10:51", updated_at: "2014-04-15 15:10:51">
irb(main):003:0> t2 = Technician.find 2
=> #<Technician id: 2, name: "Chris", created_at: "2014-04-15 15:10:57", updated_at: "2014-04-15 15:10:57">
irb(main):004:0> t1.primary_devices
  Device Load (0.5ms)  SELECT `devices`.* FROM `devices` WHERE `devices`.`technician_id` = 1
=> [#<Device id: 1, name: "Copier1", technician_id: 1, backup_tech_id: 2, created_at: "2014-04-15 15:18:23", updated_at: "2014-04-15 16:24:07">]
irb(main):005:0> t2.primary_devices
  Device Load (0.5ms)  SELECT `devices`.* FROM `devices` WHERE `devices`.`technician_id` = 2
=> []
irb(main):006:0> t2.backup_devices
  Device Load (0.5ms)  SELECT `devices`.* FROM `devices` WHERE `devices`.`backup_tech_id` = 2
=> [#<Device id: 1, name: "Copier1", technician_id: 1, backup_tech_id: 2, created_at: "2014-04-15 15:18:23", updated_at: "2014-04-15 16:24:07">]

I've left the SQL queries in the listing so you can see what's actually happening "behind-the-scenes".



#8 Jemagee

Jemagee

    Inspector

  • Members
  • 62 posts

Posted 15 April 2014 - 10:36 PM

But you still cant easily find all the techs on one device

 

(technically you could write  a scope that did it i bet)



#9 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 10:39 PM

But you still cant easily find all the techs on one device

 

(technically you could write  a scope that did it i bet)

Sure I can. There is only one primary tech and one backup tech per device and 

irb(main):007:0> d.technician
  Technician Load (0.4ms)  SELECT `technicians`.* FROM `technicians` WHERE `technicians`.`id` = 1 LIMIT 1
=> #<Technician id: 1, name: "Colin", created_at: "2014-04-15 15:10:51", updated_at: "2014-04-15 15:10:51">
irb(main):008:0> d.backup_tech
  Technician Load (0.5ms)  SELECT `technicians`.* FROM `technicians` WHERE `technicians`.`id` = 2 LIMIT 1
=> #<Technician id: 2, name: "Chris", created_at: "2014-04-15 15:10:57", updated_at: "2014-04-15 15:10:57">

gets me what I need. :)

 

I suppose to be strictly "symmetrical", I should rewrite the model definitions as

class Device < ActiveRecord::Base
  attr_accessible :name, :technician_id, :backup_tech_id
  belongs_to :primary_tech, :class_name => 'Technician', :foreign_key => 'technician_id'
  belongs_to :backup_tech, :class_name => 'Technician', :foreign_key => 'backup_tech_id'
end



#10 Jemagee

Jemagee

    Inspector

  • Members
  • 62 posts

Posted 15 April 2014 - 10:42 PM

Thanks for asking this - i have some designs that have rather 'deep' complex relations (one time I wrote a query that referenced the origin table, twice, for necessary reasons), and I want to follow best practices - so this whole 'don't next more than once' thing is going to be complex for me



#11 colinwu

colinwu

    Passenger

  • Members
  • 7 posts

Posted 15 April 2014 - 10:46 PM

You're welcome. I have a few projects that I should probably revisit too. :)

 

ActiveRecord associations can be a bit of a trial figuring out, but once you get it right things just work so much better. I love RoR!






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users