Ruby-on-rails – ActiveRecord relationships for a join table linking two records of the same table


I have a Character model and a Link model. The Link model represents a link from a character to another character. A link has a textual 'description' attribute. A link from character A to character B is distinct from the opposite link from B to A. A character has zero or one link to another character. A character may have various links to different characters. A character may be linked to by various different characters.

I used used Active Record relationships to partly implement relationships between Character and Link models:

class Character 
 has_many :links # the links from the character to other characters

class Link 
 belongs_to :character # the character from which starts the link to another character

which give me useful methods like character.links (array of all links starting from this character) or link.character (character from which starts the link)

The link model has also a to_character_id which contains the id of the character to who goes the link. Thus a link from character A to character B is an instance with the following attributes:

  • character_id = id of character A
  • to_character_id = id of character B
  • description = some text

I've written various extra methods like character.links_to (returning an array of all links which points to the character) or link.to_character (returning the character to which points the link), or character.characters_who_link_to (returning an array of the other characters having a link to this character). I've written also a callback to make sure that when a character is deleted, all links which go to this character are deleted (same for restoration).

Is possible to use additional AR relationships declarations which would provide me with this kind of extra methods, so that I do not have to write myself those methods and callbacks?

Agile Web Development with Rails presents a solution in the section "Using Models as Join Tables" but for a join table joining two different tables.
In my case my join table Links join records of a single table, Characters.

Best Solution

has_and_belongs_to_many is not really in use anymore; I'd use has_many :through instead.

class Character < ActiveRecord::Base
   has_many :links, :dependent => destroy
   has_many :characters, :through => :links 

   has_many :source_links, :class_name => "Link", 
     :foreign_key => "to_character_id", :dependent => :destroy
   has_many :source_characters, :class_name => "Character", 
     :through => :destination_links

 class Link < ActiveRecord::Base
   belongs_to :character
   belongs_to :source_character, :class_name => "Character", 
    :foreign_key => "to_character_id"

Note the :dependent => :destroy options - these will delete the links when the character is deleted. The naming is right - from the character's point of view, source_links are links to that character. So now you can do:

@character.characters # characters I link to
@character.links # links I have to other characters
@character.source_characters # characters that link to me
@character.source_links # links other characters have to me
Related Question