Модель Rails с Foreign_key и таблицей ссылок - PullRequest
1 голос
/ 08 марта 2009

Я пытаюсь создать модель для проекта ruby ​​on rails, которая строит отношения между разными словами. Думайте об этом как о словаре, где «Связи» между двумя словами показывают, что их можно использовать как синоним Моя БД выглядит примерно так:

Words
----
id

Links
-----
id
word1_id
word2_id

Как создать связь между двумя словами, используя таблицу ссылок. Я пытался создать модель, но не знал, как запустить таблицу ссылок:

class Word < ActiveRecord::Base
  has_many :synonyms, :class_name => 'Word', :foreign_key => 'word1_id'
end

Ответы [ 5 ]

4 голосов
/ 08 марта 2009

Как правило, если ваша ассоциация имеет суффиксы, такие как 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
2 голосов
/ 09 марта 2009

Слово модель:

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 на has_many :links удалит все ссылки, связанные с этим словом, перед 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"))
2 голосов
/ 08 марта 2009

Хм, это сложно. Это потому, что синонимы могут быть либо из идентификатора word1, либо из word2, либо из обоих.

В любом случае, при использовании модели для таблицы ссылок необходимо использовать параметр: 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

|| Объединение результатов объединит массивы и устранит дубликаты между ними.

* Этот код не проверен.

1 голос
/ 08 марта 2009

Я бы посмотрел на это под другим углом; так как все слова являются синонимами, вы не должны рекламировать одно из них как «лучшее» Попробуйте что-то вроде этого:

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
0 голосов
/ 30 апреля 2011

Пытаясь реализовать решение Сары, я столкнулся с двумя проблемами:

Во-первых, решение не работает при желании назначить синонимы, выполнив

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

Также косвенное удаление синонимов не работает должным образом. Это связано с тем, что Rails не запускает обратные вызовы after_save_on_create и after_destroy при автоматическом создании или удалении записей Link. По крайней мере, в Rails 2.3.5, где я это попробовал.

Это можно исправить с помощью обратных вызовов: after_add и: after_remove в модели Word:

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

Где обратные вызовы - это методы Сары, слегка скорректированные:

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

Вторая проблема решения Сары заключается в том, что синонимы, которые уже есть у других слов, когда они связаны вместе с новым словом, не добавляются к новому слову и наоборот. Вот небольшая модификация, которая решает эту проблему и гарантирует, что все синонимы группы всегда связаны со всеми другими синонимами в этой группе:

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 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...