Rails has_many_polymorphs в обратном порядке? - PullRequest
1 голос
/ 23 января 2009

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

Например:

class Project < ActiveRecord::Base
end

class Message < ActiveRecord::Base
  has_many_polymorphs :consumers, 
    :from => [:projects, :messages], 
    :through  => :message_consumers,
    :as => :comment   # Self-referential associations have to rename the non-polymorphic key
end

class MessageConsumer < ActiveRecord::Base
  # Self-referential associations have to rename the non-polymorphic key
  belongs_to :comment, :foreign_key => 'comment_id', :class_name => 'Message'

  belongs_to :consumer, :polymorphic => true
end

В этом случае Сообщение не будет удалено при удалении Проекта, потому что Сообщение действительно является родителем в отношениях.

Я немного упростил это для примера, но есть другие модели, у которых есть Сообщение, и есть также Приложения, которые работают аналогично.

Каков будет правильный способ настроить это так, чтобы потомки удалялись при удалении родителя? Я надеюсь, что у меня не будет миллиона таблиц, но я не могу придумать другого способа сделать это.

Ответы [ 2 ]

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

Когда вы говорите «чтобы дети удалялись, когда родитель удалялся?», Вы можете привести пример? То есть когда проект удален, я хочу, чтобы все его сообщения тоже были удалены? Что происходит, когда вы удаляете сообщение, хотите ли вы удалить что-либо еще (например, все соответствующие записи message_consumer)?


UPDATE

ОК, поэтому has_many_polymorphs автоматически удалит «осиротевшие» message_consumer с. Ваша проблема в том, что сообщение может иметь более одного потребителя, поэтому удаление проекта может быть недостаточным основанием для удаления всех связанных с ним сообщений (поскольку другие потребители могут зависеть от этих сообщений).

В этом конкретном случае вы можете настроить обратный вызов after_destroy в MessageConsumer, чтобы проверить, существуют ли еще другие MessageConsumer отображения (кроме себя), которые ссылаются на Message, и, если таковые не существуют, также удалите сообщение, например:

class MessageConsumer < ActiveRecord::Base

  ...

  after_destroy :delete_orphaned_messages

  def delete_orphaned_messages
    if MessageConsumer.find(:first, :conditions => [ 'comment_id = ?', self.comment_id] ).empty?
      self.comment.delete
    end
  end
end

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

Вам следует знать только о возможных условиях гонки, при которых один сеанс может прийти к выводу, что Message больше не используется, тогда как другой может находиться в процессе создания нового MessageConsumer для того же самого Message. Это может быть обеспечено ссылочной целостностью на уровне БД (добавьте ограничения внешнего ключа, которые приведут к сбою двух сеансов и сохранят вашу базу данных в согласованном состоянии) или блокировкой (тьфу!)

0 голосов
/ 15 марта 2009

Вы можете упростить это, используя acts_as_commentable.

...