Рельсы полиморфные ассоциации многие ко многим - PullRequest
3 голосов
/ 05 июня 2009

Я пытаюсь настроить общий вид сети связанных объектов. Допустим, у меня есть 4 модели.

  • Книга
  • Фильм
  • Tag
  • Категория

Я бы хотел сделать:

book = Book.find(1)
book.relations << Tag.find(2)
book.relations << Category.find(3)
book.relations #=> [Tag#2, Category#3]

movie = Movie.find(4)
movie.relations << book
movie.relations << Tag.find(5)
movie.relations #=> [Book#1, Tag#5]

По сути, я хочу иметь возможность взять любые 2 объекта любого модельного класса (или класса модели, который я разрешаю) и объявить, что они связаны.

Очевидно, я не хочу создавать огромную путаницу таблиц соединений. Кажется, что это не совсем имеет много через ассоциацию, и не совсем полиморфная ассоциация.

Это то, что Rails может поддерживать посредством деклараций об ассоциации, или я должен использовать здесь свою собственную логику?

Ответы [ 4 ]

7 голосов
/ 05 июня 2009

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

class Relation
  belongs_to :owner, :polymorphic => true
  belongs_to :child_item, :polymorphic => true
end

class Book
  has_many :pwned_relations, :as => :owner, :class_name => 'Relation'
  has_many :pwning_relations, :as => :child_item, :class_name => 'Relation'

  # and so on for each type of relation
  has_many :pwned_movies, :through => :pwned_relations, 
           :source => :child_item, :source_type => 'Movie'
  has_many :pwning_movies, :through => :pwning_relations, 
           :source => :owner, :source_type => 'Movie'
end

Недостатком такого рода структуры данных является то, что вы вынуждены создавать две разные роли для того, что может быть равным соединением. Если я хочу посмотреть все связанные фильмы для моей Книги, я должен сложить наборы вместе:

( pwned_movies + pwning_movies ).uniq

Типичным примером этой проблемы являются отношения «друг» в приложениях социальных сетей. Одним из решений, используемых Insoshi, является регистрация обратного вызова after_create в модели соединения (в данном случае Relation), которая создает обратную связь. Обратный вызов after_destroy был бы аналогичным образом необходим, но при этом за счет некоторого дополнительного хранилища БД вы можете быть уверены, что вы получите все связанные фильмы в одном запросе БД.

class Relation
  after_create do 
    unless Relation.first :conditions => 
      [ 'owner_id = ? and owner_type = ? and child_item_id = ? and child_item_type = ?',       child_item_id, child_item_type, owner_id, owner_type ]
      Relation.create :owner => child_item, :child_item => owner
    end
  end
end
3 голосов
/ 05 июня 2009

Я придумал немного решения. Я не уверен, что это лучшее, однако. Кажется, у вас не может быть полиморфного сквозного has_many.

Итак, я немного притворяюсь. Но это означает отказ от магии прокси ассоциации, которую я так люблю, и это меня огорчает. В базовом состоянии вот как это работает.

book = Book.find(1)
book.add_related(Tag.find(2))
book.add_related(Category.find(3))
book.related        #=> [Tag#2, Category#3]
book.related(:tags) #=> [Tag#2]

Я завернул его в модуль многократного использования, который можно добавить в любой класс модели с помощью одного has_relations метода класса.

http://gist.github.com/123966

Я действительно надеюсь, что мне не нужно полностью заново реализовывать прокси-сервер ассоциации, чтобы работать с этим.

0 голосов
/ 05 июня 2009

в зависимости от того, насколько тесно связаны ваши таблицы фильмов / книг.

что если вы объявите

class Items < ActiveRecord::Base
  has_many :tags
  has_many :categories
  has_and_belongs_to_many :related_items,
       :class => "Items",
       :join_table => :related_items,
       :foreign_key => "item_id",
       :associated_foreign_key => "related_item_id"
end

class Books < Items
class Movies < Items

убедитесь, что вы указали тип в таблице предметов

0 голосов
/ 05 июня 2009

Я думаю, что единственный способ сделать это именно так, как вы описали, - это таблицы соединений. Это не так уж плохо, всего 6, и вы можете в значительной степени установить и забыть их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...