Reputation: 3513
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 textI'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.
Upvotes: 2
Views: 6123
Reputation: 18484
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
end
class Link < ActiveRecord::Base
belongs_to :character
belongs_to :source_character, :class_name => "Character",
:foreign_key => "to_character_id"
end
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
Upvotes: 4
Reputation: 3043
I think what you are really going for here is a self referential HABTM relationship using a join table
so if you had a join table
create_table :character_links do |t|
t.integer :character_id
t.integer :linked_character_id
t.timestamps #if you want to know when the relationship was created
end
Then you would have
class Characters < ActiveRecord::Base
has_and_belongs_to_many :linked_characters,
:class_name => "Characters",
:join_table => :character_links,
:foreign_key => "character_id",
:associated_foreign_key => "linked_character_id"
if you needed outgoing links and incoming links then you could just do
class Characters < ActiveRecord::Base
has_and_belongs_to_many :outgoing_links,
:class_name => "Characters",
:join_table => :character_links,
:foreign_key => "character_id",
:associated_foreign_key => "linked_character_id"
has_and_belongs_to_many :incoming_links,
:class_name => "Characters",
:join_table => :character_links,
:foreign_key => "linked_character_id",
:associated_foreign_key => "character_id"
Just switched the foreign_key and associated_foreign_key
This eliminates the need for you to have a seperate Links model
This is air code(not tested)
Upvotes: 0
Reputation: 151
I think you want something like the following:
class Character < ActiveRecord::Base
has_many :outbound_links, :class_name => "Link", :foreign_key => "from_character_id"
has_many :inbound_links, :class_name => "Link", :foreign_key => "to_character_id"
end
class Link < ActiveRecord::Base
belongs_to :from_character, :class_name => "Character", :foreign_key => "from_character_id"
belongs_to :to_character, :class_name => "Character", :foreign_key => "to_character_id"
end
You can read about all your options on ActiveRecord associations at http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Upvotes: 2