使用foreign_key和链接表的Rails模型

我正在尝试为rails项目上的ruby创建一个模型,以建立不同单词之间的关系。 可以把它想象成一个字典,其中两个单词之间的“链接”表明它们可以同义使用。 我的数据库看起来像这样:

Words ---- id Links ----- id word1_id word2_id 

如何使用链接表在两个单词之间创建关系。 我试图创建模型,但不知道如何使链接表发挥作用:

 class Word  'Word', :foreign_key => 'word1_id' end 

通常,如果您的关联具有后缀,例如1和2,则说明它没有正确设置。 试试这个Word模型:

 class Word < ActiveRecord::Base has_many :links, :dependent => :destroy has_many :synonyms, :through => :links end 

链接模型:

 class Link < ActiveRecord::Base belongs_to :word belongs_to :synonym, :class_name => 'Word' # Creates the complementary link automatically - this means all synonymous # relationships are represented in @word.synonyms def after_save_on_create if find_complement.nil? Link.new(:word => synonym, :synonym => word).save end end # Deletes the complementary link automatically. def after_destroy if complement = find_complement complement.destroy end end protected def find_complement Link.find(:first, :conditions => ["word_id = ? and synonym_id = ?", synonym.id, word.id]) end end 

表:

 Words ---- id Links ----- id word_id synonym_id 

嗯,这是一个棘手的问题。 这是因为同义词可以来自word1 id或word2 id或两者。

无论如何,当使用模型作为链接表时,必须在使用链接表的模型上使用:through选项

 class Word < ActiveRecord::Base has_many :links1, :class_name => 'Link', :foreign_key => 'word1_id' has_many :synonyms1, :through => :links1, :source => :word has_many :links2, :class_name => 'Link', :foreign_key => 'word2_id' has_many :synonyms2, :through => :links2, :source => :word end 

应该这样做,但现在你必须检查两个地方以获得所有的同义词。 我会在类Word中添加一个加入这些的方法。

 def synonyms return synonyms1 || synonyms2 end 

将结果放在一起将加入数组并消除它们之间的重复。

*此代码未经测试。

单词模型:

 class Word < ActiveRecord::Base has_many :links, :dependent => :destroy has_many :synonyms, :through => :links def link_to(word) synonyms << word word.synonyms << self end end 

设置:dependent => :destroy on has_many :links会在destroy单词record之前destroy与该单词关联的所有链接。

链接模型:

 class Link < ActiveRecord::Base belongs_to :word belongs_to :synonym, :class_name => "Word" end 

假设您使用的是最新的Rails,则不必为belongs_to :synonym指定外键。 如果我没记错的话,这是作为Rails 2中的标准引入的。

单词表:

 name 

链接表:

 word_id synonym_id 

要将现有单词作为同义词链接到另一个单词:

 word = Word.find_by_name("feline") word.link_to(Word.find_by_name("cat")) 

要创建一个新单词作为另一个单词的同义词:

 word = Word.find_by_name("canine") word.link_to(Word.create(:name => "dog")) 

我从不同的角度来看待它; 因为所有单词都是同义词,所以不应该宣传其中任何一个是“最好的”。 尝试这样的事情:

 class Concept < ActiveRecord::Base has_many :words end class Word < ActiveRecord::Base belongs_to :concept validates_presence_of :text validates_uniqueness_of :text, :scope => :concept_id # A sophisticated association would be better than this. def synonyms concept.words - [self] end end 

现在你可以做到

 word = Word.find_by_text("epiphany") word.synonyms 

试图实现Sarah的解决方案我遇到了两个问题:

首先,当想要通过执行分配同义词时,解决方案不起作用

 word.synonyms << s1 or word.synonyms = [s1,s2] 

间接删除同义词也无法正常工作。 这是因为当Rails自动创建或删除链接记录时,它不会触发after_save_on_create和after_destroy回调。 至少在我试过它的Rails 2.3.5中没有。

这可以通过在Word模型中使用:after_add和:after_remove回调来修复:

 has_many :synonyms, :through => :links, :after_add => :after_add_synonym, :after_remove => :after_remove_synonym 

回调是Sarah的方法,略微调整:

 def after_add_synonym synonym if find_synonym_complement(synonym).nil? Link.new(:word => synonym, :synonym => self).save end end def after_remove_synonym synonym if complement = find_synonym_complement(synonym) complement.destroy end end protected def find_synonym_complement synonym Link.find(:first, :conditions => ["word_id = ? and synonym_id = ?", synonym.id, self.id]) end 

Sarah解决方案的第二个问题是,当与新单词链接在一起时,其他单词已经具有的同义词不会添加到新单词中,反之亦然。 这是一个小修改,可以修复此问题,并确保组的所有同义词始终链接到该组中的所有其他同义词:

 def after_add_synonym synonym for other_synonym in self.synonyms synonym.synonyms << other_synonym if other_synonym != synonym and !synonym.synonyms.include?(other_synonym) end if find_synonym_complement(synonym).nil? Link.new(:word => synonym, :synonym => self).save end end