14

I've been playing around with STI and belongs_to / has_many relationships and I'm a bit confused.

I have a few questions based on a model configuration similar to:

class Parental < ActiveRecord::Base
end

class Mother < Parental
    has_many :babies
end

class Father < Parental
    has_many :babies
end

class Baby < ActiveRecord::Base
    belongs_to :?????? 
end
  1. What should Baby belong_to?
  2. In terms of a migration, what should i name/add for foreign key on the babies table?
  3. I've had a hard time researching this, is there a definitive source that explains this? The API docs did not seem to hit it on the head OR i missed it (which is totally possible).

My first thought is add parental_id to babies along with a method like Baby#owner that does the following:

  • Hits self.parental
  • Determines the parental's type
  • Returns the correct type of parental (could be a mother, could be a father)

Thank you!

Mario Zigliotto
  • 8,315
  • 7
  • 52
  • 71

3 Answers3

9

The Baby belongs to both Mother and Father

belongs_to :mother
belongs_to :father

You can have multiple foreign keys. The Baby DB table then has two fields, mother_id and father_id

The definitive guide to associations is here: http://guides.rubyonrails.org/association_basics.html

The migration to create the Baby class would look something like this:

class CreateBabies < ActiveRecord::Migration
  def self.up
    create_table :babies do |t|
      t.integer :father_id
      t.integer :mother_id
    end
  end

  def self.down
    drop_table :babies
  end
end

This gives you things like: baby.mother and baby.father. You can't have a single parental_id because the foreign key can only point to one other record, meaning that babies would only have one parent (when really they have two).

Seems like, in this case, you're just misunderstanding the relationship, is all. You are on the right track.

jefflunt
  • 33,527
  • 7
  • 88
  • 126
  • Thank you for responding. Could i bother you to look @ my update and comment on that solution? seems like less table clutter, but might just be totally wrong. – Mario Zigliotto Sep 17 '11 at 19:05
  • Sure, I commented on the `parental_id` solution, which won't work. – jefflunt Sep 17 '11 at 19:09
  • AH! That makes complete sense. Thanks again. I marked your answer as the solution. I do have one follow up q that hopefully isn't too much bother. As you mentioned, in this situation you really do need TWO owners as a child (aside from Jesus) will always have two parents. What do you do when the object under ownership can only belong to 1 owner? For example, say a `Post` and the STI setup is `Author`, `LivingAuthor < Author`, `DeadAuthor < Author` ? – Mario Zigliotto Sep 17 '11 at 19:14
  • I can create a new question if you want the points. Let me know. – Mario Zigliotto Sep 17 '11 at 19:15
  • Well, I don't know why you would have two classes, one for `LivingAuthor` and one for `DeadAuthor`. Instead just have an `Author` with a boolean field `alive` that is true if the person is living, and false if they are deceased. Also, if you want to give me more rep, just upvote the answer I've already given. You can upvote in addition to accepting. – jefflunt Sep 17 '11 at 19:18
  • 2
    Doesn't Jesus have at least two Parents? Mary, and God? Your model doesn't make a distinction between biological father (God?) vs. step/adopted father (Joseph). If you need to handle step/adopted parents as well, then you'll have to change it to a `has_and_belongs_to_many :mothers` and `has_and_belongs_to_many :fathers` relationship. – jefflunt Sep 17 '11 at 19:24
  • Well my example appears to have been crap, but the main point is I'm curious how to deal with a situation where there are a number of potential owners but only one can own at a time. Using the crappy `LivingAuthor` vs `DeadAuthor` STI example i could the `posts` gets `living_author_id` and `dead_author_id` solution working but when you want to find the exclusive owner one would have to grab the `Post` from the db check which kind of author owns it, then go find that author and return it. – Mario Zigliotto Sep 17 '11 at 19:25
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3559/discussion-between-normalocity-and-sizzlepants) – jefflunt Sep 17 '11 at 19:27
  • Let's say for the same of argument that a Baby can only have one parent, but you don't know whether it's a Mother or a Father. How would you handle this scenario? I'm having trouble with nested attributes in this case, because the association is on Mother and Father, and not Parental model – elsurudo Apr 11 '17 at 08:07
  • It's been many years, but I think I would take a different approach if answering this question anew today. The main issue is that kinship relationships are specific to the culture in question, and if you were modeling people from many different cultures, you might need something more generic. E.g. a person has many `relations` and each relation has data on it to tell you what kind of kin it is (parent, uncle, nephew, etc.). This is just a quick idea without much thought, but human relationships are quite complex, so it depends a lot on your specific needs. – jefflunt Apr 12 '17 at 03:01
  • @elsurudo - but to answer your parent without a known gender question (or think same-sex relationships where both parents are the same gender), it might be easier to do something like `has_many parents` where the gender of the parent is an optional field, and there could be any number of parents. – jefflunt Apr 12 '17 at 03:03
3

I've solved a similar problem myself by adding an explicit foreign_key call.

Something like the following code:

class Parental < ActiveRecord::Base
end

class Mother < Parental
    has_many :babies
end

class Father < Parental
    has_many :babies
end

class Baby < ActiveRecord::Base
    belongs_to :mother, foreign_key: 'parental_id'
    belongs_to :father, foreign_key: 'parental_id'
end

Of course, this assumes that a baby has only one parent. :-)

akoller
  • 79
  • 6
  • 2
    Why not just use `belongs_to :parent` then ? – Gryfith Sep 25 '18 at 12:30
  • 1
    @Gryfith this won't work, ActiveRecord will try to build a query with something like `WHERE babies.mother_id...`. – Rich Steinmetz Sep 11 '22 at 13:49
  • 1
    I don't think so, but actually, my answer is still wrong but because you won't be able to differentiate `mother` and `father` as you'll have only one dependency to parental model. And the answer from akoller is also wrong, since he's trying to match two associations to the same column, which will definitely not work as he thinks – Gryfith Oct 14 '22 at 17:11
0

To also get records in the other directions you'll also need to add foreign_key: 'parental_id' on the has_many relationships to do something like Mother.some_scope.preload(:babies).

class Parental < ActiveRecord::Base
end

class Mother < Parental
    has_many :babies, foreign_key: 'parental_id'
end

class Father < Parental
    has_many :babies, foreign_key: 'parental_id'
end

class Baby < ActiveRecord::Base
    belongs_to :mother, foreign_key: 'parental_id'
    belongs_to :father, foreign_key: 'parental_id'
end

And you still need belongs_to :mother, foreign_key: 'parental_id' otherwise ActiveRecord will try to build a query with WHERE babies.mother_id....

Rich Steinmetz
  • 1,020
  • 13
  • 28