0

How do I access universe from actor (and vice versa)?

I have three models universe character actor

Universes and characters are associated via a separate join table using has_many through. Characters and actors are associated via a separate join table using has_many through.

(Note: the reason why i decided against has_and_belongs_to_many and instead chose has_many through is because of reasons outlined in the accepted answer Rails habtm joins )

I do not wish to store foreign keys in any of the three primary tables.

universe
    \
     \
     universe_character_tie
     /
    /
character
    \
     \
     character_actor_tie
     /
    /
actor

I want to somehow associate universes and actors... through characters. Two approaches come to mind:

1) If I do a has_many through characters type of association, that will

  1. bypass the other two has_many through join tables; and
  2. require foreign keys in `characters, right?

2) Don't do anything. calling actor.character.universe will already work... (is this true)?

I am not sure whether my haunches are correct, and I am somewhat stuck in coding it. Can someone shed light, please?

Community
  • 1
  • 1
ahnbizcad
  • 10,491
  • 9
  • 59
  • 85
  • 1
    `actor.character.universe` won't work because rails won't know which character you want or which universe – j-dexx May 14 '14 at 09:02
  • you're right. I am being way too loose with my wording. assuming that i specify which actor and which universe, what would the call code look like in the current setup of two has_many through relationships (if it's even possible)? – ahnbizcad May 14 '14 at 09:04

2 Answers2

1

Forgive me if this is written as an answer as I have not tested the following. I believe you can nest the associations as long as you declare them like

class Actor < ActiveRecord::Base
  has_many :character_actor_ties
  has_many :characters, through: :character_actor_ties
  has_many :universe_character_ties, through: :characters
  has_many :universes, through: :universe_character_ties
end

Then just call actor.universes to get all universes that are associated to an actor's characters.

EDIT (explanation)

The code above relies heavily on rails' naming convention. From the rails api, the through option

Specifies an association through which to perform the query. This can be any other type of association, including other :through associations. Options for :class_name, :primary_key and :foreign_key are ignored, as the association uses the source reflection.

That means that calling actor.characters will go through character_actor_ties and look for an association named character and will use the rules declared on that association to build the sql query associating an actor and character.

Just follow this pattern until you get to a model that is associated directly to universes and that is universe_character_ties.

jvnill
  • 29,479
  • 4
  • 83
  • 86
  • I did wonder if this would work as I have a similar setup on one of my projects but only with one has_many through. Would be awesome if it works – j-dexx May 14 '14 at 09:14
  • Can you explain a bit of what goes on in the background of `has_many :universe_character_ties, through: :characters` `has_many :universes, through: :universe_character_ties` – ahnbizcad May 14 '14 at 09:16
  • do the last two lines (before `end`) necessitate additional foreign keys anywhere? – ahnbizcad May 14 '14 at 09:22
  • nope, they don't need additional foreign keys anywhere. – jvnill May 14 '14 at 09:27
1

This should work:

class Universe < ActiveRecord::Base
  has_many :universe_character_ties
  has_many :characters, through: :universe_character_ties

  has_many :actors, through: :characters
end

class UniverseCharacterTie < ActiveRecord::Base
  belongs_to :universe
  belongs_to :character
end

class Character < ActiveRecord::Base
  has_many :universe_character_ties
  has_many :universes, through: :universe_character_ties

  has_many :character_actor_ties
  has_many :actors, through: :character_actor_ties
end

class CharacterActorTie < ActiveRecord::Base
  belongs_to :actor
  belongs_to :character
end

class Actor < ActiveRecord::Base
  has_many :character_actor_ties
  has_many :characters, through: :character_actor_ties

  has_many :universes, through: :characters
end

Then you can call actor.universes or universe.actors ...

Hope this helps

j-dexx
  • 10,286
  • 3
  • 23
  • 36
CupraR_On_Rails
  • 2,449
  • 1
  • 19
  • 24
  • change `throught` to `through` :) – jvnill May 14 '14 at 09:18
  • Oups you're right. And sorry @jvnill I didn't see your answer before I posted mine... – CupraR_On_Rails May 14 '14 at 09:22
  • awesome, so from what I already have, i only need to add `has_many :actors, through: :characters` to Universe, and `has_many :universes, through: :characters` to Actor. =D – ahnbizcad May 14 '14 at 09:26
  • How does rails make `through: :characters` work, even if characters doesn't contain any foreign keys? does using `through` only apply for helper methods? Does it know to access the other two join tables? – ahnbizcad May 17 '14 at 06:39