ИППП и полиморфы

У меня проблема с моим кодом

class Post < ActiveRecord::Base
end

class NewsArticle < Post
  has_many :comments, :as => :commentable, :dependent => :destroy, :order => 'created_at'
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true, :counter_cache => true
end

И в попытке пойдите, чтобы получить комментарии к некоторым NewsArticle, я вижу в журналах что-то вроде

  Comment Load (0.9ms)   SELECT "comments".* FROM "comments" WHERE ("comments"."commentable_id" = 1 and "comments"."commentable_type" = 'Post') ORDER BY created_at

Странно, что "commentable_type" = 'Post'. Что не так?

PS: Rails 2.3.5 && ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10]

Ответ 1

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

Итак:

Здесь комментарий указывает на таблицу, на которую он комментирует. Таблица posts, id 1

>> Comment.first
=> #<Comment id: 1, commentable_id: 1, commentable_type: "Post", body: "test", created_at: "2010-04-09 00:56:36", updated_at: "2010-04-09 00:56:36">

Затем для загрузки NewsArticle id 1 загружается из сообщений, а тип там указывает на статью News.

>> Comment.first.commentable
=> #<NewsArticle id: 1, type: "NewsArticle", name: "one", body: "body", created_at: "2010-04-09 00:55:35", updated_at: "2010-04-09 00:55:35">
>> Comment.first.commentable.class.table_name
=> "posts"

Если commentable_type сохранено "NewsArticle", ему придется посмотреть на класс, чтобы определить таблицу. Таким образом, он может просто смотреть на стол и беспокоиться о типе, когда он туда попадает.

Ответ 2

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

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true, :counter_cache => true

  def commentable_type=(sType)
   super(sType.to_s.classify.constantize.base_class.to_s)
  end
end

class Post < ActiveRecord::Base
  has_many :comments, :as => :commentable, :dependent => :destroy, :order => 'created_at'
end

class NewsArticle < Post
end

Ответ 3

def commentable_type = (sType)  супер (sType.to_s.classify.constantize.base_class.to_s) конец

Этот метод возвращает класс как Post, что делать, если вы хотите сохранить унаследованный класс Post в качестве commentable_type?

Ответ 4

Хороший вопрос. У меня была такая же проблема с Rails 3.1. Похоже, проблема еще не решена. По-видимому, использование полиморфных ассоциаций в сочетании с однонаправленным наследованием (STI) в Rails является немного сложным.

Текущая документация Rails для Rails 3.2 дает этот совет для объединения полиморфных ассоциаций и STI:

Использование полиморфных ассоциаций в сочетании с одной таблицей Наследование (STI) немного сложно. Для того чтобы ассоциации работайте, как ожидалось, убедитесь, что вы храните базовую модель для ИППП модели в столбце типа полиморфной ассоциации.

В вашем случае базовой моделью будет "Post", то есть "commentable_type" должен быть "Post" для всех комментариев.

Ответ 5

Технически, в этом нет ничего плохого. Когда Rails имеет дело с полиморфной ассоциацией, а связанный с ним объект использует STI, он просто использует базовый класс как тип (в вашем случае "commentable_type" ).

Если у вас есть Post и NewsArticle в отдельных таблицах, очевидно, commentable_type будет отображаться как Post и NewsArticle соответственно.