2

I need to model up to 5 or 6 generations horse pedigrees using rails/activerecord. I did my research here on stack and on the web and ultimately utilized this article as the basis of my approach. Here's what I've come up with.

Two models:

Horse has the following attributes id and horse_name
Pedigree has: id, parent_id and horse_id.

And the following associations:

has_many  :parent_horse_relationships, :class_name => "Pedigree", :foreign_key => :horse_id, :dependent => :destroy

has_one  :sire_horse_relationship, :class_name => "Pedigree", :foreign_key => :horse_id, :conditions => "horse_gender = 'Male'

has_one  :dam_horse_relationship, :class_name => "Pedigree", :foreign_key => :horse_id, :conditions => "horse_gender = 'Female'

has_many  :parents, :through => :parent_horse_relationships, :source => :parent 

has_one  :sire, :through => :sire_horse_relationship,:source => :parent

has_one  :dam, :through => :dam_horse_relationship,:source => :parent

has_many  :horse_parent_relationships, :class_name => "Pedigree", :foreign_key => :parent_id, :dependent => :destroy

has_many  :progenies, :through => :horse_parent_relationships, :source =>  :horse

This approach is close, however it appears my condition to determine the dam or sire is being applied to the Horse and not the parent. Therefore if the particular horse is Male, the horse.sire will work, but the horse.dam will not and vice versa. Once I get basic functionality working I'd like to add additional methods to get the whole pedigree, grandparents, siblings, descendants, etc.

Questions:

  1. How can I apply the gender condition to the parents and not the horse so that both sire and dam work.

  2. Is the approach that I have take viable or is there a more elegant, efficient way of accomplishing this.

  3. Any other suggestions or guidance would be appreciated.

Apologies for the long question and thanks for your help.

Mutuelinvestor
  • 3,384
  • 10
  • 44
  • 75

2 Answers2

2

I might start with:

has_one  :sire, :class_name => "Pedigree", :foreign_key => :horse_id, :conditions => "horse_gender = 'Male'
has_one  :dam, :class_name => "Pedigree", :foreign_key => :horse_id, :conditions => "horse_gender = 'Female'

has_many  :parent_horse_relationships, :class_name => "Pedigree", :foreign_key => :horse_id, :dependent => :destroy
has_many  :parents, :through => :parent_horse_relationships, :source => :parent 

has_many  :progenies, :through => :horse_parent_relationships, :source =>  :horse
Michael Durrant
  • 93,410
  • 97
  • 333
  • 497
  • Thanks for the feedback. So it sounds like you don't think I need the sire_horse_relationship and dam_horse_relationship. – Mutuelinvestor Jan 31 '13 at 01:43
  • 1
    I would try and start with the simplest possible relationships and try them out before adding all the ones that you might need at the end. It looked to me like the extra relationships were representing generations and I thought that those will be stored through the relationship being recursive (not sure if an additional attribute is needed). – Michael Durrant Jan 31 '13 at 01:49
  • This may help: http://stackoverflow.com/q/5109893/631619 and vhttp://stackoverflow.com/q/3029227/631619 If you want a self-referential relationship you'll need ..._id and ..._type fields. – Michael Durrant Jan 31 '13 at 02:06
  • Thanks for the additional reference. What are your thoughts on doing this all with just the Horse model. I believe that it could work, I just can quite get my mind around the pros and cons of each approach. – Mutuelinvestor Feb 01 '13 at 03:13
  • I think that would be best. I would also consider a pedigree model which would include a foreign keys for child_horse_id and parent_horse_id which would point to horse. – Michael Durrant Feb 01 '13 at 04:08
  • Just realized the code above doesn't really work because the condition is applied to the subject horse and not the parents. As a result, sire will work for male horses but dam will not and dam will work for female horses but sire will not. How would I apply the condition to the parents. The plot thickens! – Mutuelinvestor Feb 01 '13 at 22:10
0

I ended up spending a great deal of time on this one, but finally came up with a solution that met my requirements. The associations that ultimately worked follow:

  has_many :parent_horse_relationships, :class_name => "Pedigree", :foreign_key => :horse_id, :dependent => :destroy

  has_many :parents, :through => :parent_horse_relationships, :source => :parent do 
    def dam_relationship
      owner = self.proxy_association.owner
      owner = owner.parents.where(:horse_gender => "Female")
      where('pedigrees.parent_id = ?', owner)
    end

    def sire_relationship
      owner = self.proxy_association.owner
      owner = owner.parents.where(:horse_gender => "Male")
      where('pedigrees.parent_id = ?', owner)
    end
  end

  def dam
    parents.dam_relationship
  end

  def sire
    parents.sire_relationship
  end

Question responses:

I applied the gender condition through use of an association_proxy and a simple wrapper. I created a dam_relationship and corresponding sire_relationship and then wrapped those methods in a couple of dam and sire wrapper methods.

def dam_relationship
owner = self.proxy_association.owner
owner = owner.parents.where(:horse_gender => "Female")
where('pedigrees.parent_id = ?', owner)
end

def dam
parents.dam_relationship
end

This allows me to do:

@horse.parents, @horse.dam, @horse.sire (not displayed)

as well as most of the methods included in the ancestry gem mentioned below. With a little bit of recursion it's fairly straight forward to display the entire pedigree or the number of generations that interest you.

I decided that the approach of having two models (Horse and Pedigree) provide som additional flexibility compared to having the sire_id and dam_id directly in the Horse model. This approach will enable me to more easily create methods like @horse.uncle, @horse.aunt. I believe these would be more difficult with the sire_id and dam_id directly in the Horse model.

The most popular gem for accomplishing this seems to be ancestry. The author accomplishes this and a lot more simply by adding an ancestry column to the model of interest. Its a very nice solution a definitely worth checking out.

Mutuelinvestor
  • 3,384
  • 10
  • 44
  • 75