Я сталкивался с этой проблемой данных несколько раз и пробовал несколько разных стратегий. Я думаю, что я самый большой поклонник, это подход STI, как упомянуто cicloon. Убедитесь, что в вашей таблице записей есть столбец type
.
class Blog < ActiveRecord::Base
# this is your generic association that would return all types of entries
has_many :entries
# you can also add other associations specific to each type.
# through STI, rails is aware that a media_entry is in fact an Entry
# and will do most of the work for you. These will automatically do what cicloon.
# did manually via his methods.
has_many :articles
has_many :quotes
has_many :media
end
class Entry < ActiveRecord::Base
end
class Article < Entry
has_one :article_data
end
class Quote < Entry
has_one :quote_data
end
class Media < Entry
has_one :media_data
end
class ArticleData < ActiveRecord::Base
belongs_to :article # smart enough to know this is actually an entry
end
class QuoteData < ActiveRecord::Base
belongs_to :quote
end
class MediaData < ActiveRecord::Base
belongs_to :media
end
Что мне нравится в этом подходе, так это то, что вы можете хранить общие данные Entry в модели ввода. Абстрагируйте любые данные типа вложенных записей в их собственные таблицы данных и свяжите их с has_one, в результате чего в вашей таблице записей не будет лишних столбцов. Это также работает очень хорошо, когда вы делаете ваши взгляды:
app/views/articles/_article.html.erb
app/views/quotes/_quote.html.erb
app/views/media/_media.html.erb # may be medium here....
и из ваших представлений вы можете сделать либо:
<%= render @blog.entries %> <!-- this will automatically render the appropriate view partial -->
или больше контроля:
<%= render @blog.quotes %>
<%= render @blog.articles %>
Вы также можете найти довольно общий способ генерации форм, я обычно отображаю общие поля ввода в частичном entries/_form.html.erb
. Внутри этой части у меня также есть
<%= form_for @entry do |f| %>
<%= render :partial => "#{f.object.class.name.tableize}/#{f.object.class.name.underscore}_form", :object => f %>
<% end %>
тип визуализации для данных формы. В свою очередь, подформы могут использовать accepts_nested_attributes_for
+ fields_for
для правильной передачи данных.
Единственная боль, которую я испытываю при таком подходе, заключается в том, как обращаться с контроллерами и помощниками маршрутов. Поскольку каждая запись имеет свой собственный тип, вам придется либо создавать собственные контроллеры / маршруты для каждого типа (вы можете захотеть это ...), либо создавать общий. Если вы выберете общий подход, запомните две вещи.
1) Вы не можете установить поле :type
через атрибуты обновления, ваш контроллер должен будет создать соответствующий Article.new
, чтобы сохранить его (вы можете использовать фабрику здесь).
2) Вам придется использовать метод becomes()
(@article.becomes(Entry)
) для работы с записью в качестве Записи, а не подклассом.
Надеюсь, это поможет.
Предупреждение. Я действительно использовал Media в качестве названия модели в прошлом. В моем случае это привело к созданию таблицы, называемой medias в rails 2.3.x, однако в rails 3 она хотела, чтобы моя модель называлась Medium, а мой table media. Возможно, вам придется добавить собственный Inflection для этого имени, хотя я не уверен.