#5 楼 @Rei ,我帖上我的解决方案吧。我是先用这个方案解决了问题。连帖子都写好了。准备帖在分享栏的。但是想想觉得太丑陋了。于是才发帖问有没有通用的解决方案的:
Mongoid + Rails STI(Single Table Inheritance)
Models
class Category
include Mongoid::Document
field :name
has_many :products
has_many :pages
end
class Pagecategory < Category
end
class Prodcategory < Category
end
class Page
include Mongoid::Document
field :name
field :content
belongs_to :category
end
class Product
include Mongoid::Document
field :name
field :description
belongs_to :category
end
Prodcategory 继承自 Category, 创建 Prodcategory 实例后,会将其在座存储于 Category 表中。同时在 Category 表中添加 _type 对象。
mongoid.org 的描述如下:An additional attribute _type is stored in order to make sure when loaded from the database the correct document is returned.
详情参见http://mongoid.org/docs/documents/inheritance.html
如Prodcategory.create(name: 'product category 1')
在 mongodb 中实际保存在 Category 表中,其_type 为'Prodcategory'.
这时,没有必要专门去创建 prodcategories_controller.rb 与 pagecategories_controller.rb 以及相应的 views。
所要做的事情是对 routes.rb , views/categories/_form.html.erb 及 controllers/categories_controller.rb进行一些修改。
修改后的 routes.rb
resources :pages
resources :products
resources :categories
resources :prodcategories, :controller => "categories",:_type => "Prodcategory"
resources :pagecategories, :controller => "categories",:_type => "Prodcategory"
修改后的 views/categories/_form.html.erb
<%= simple_form_for(@category) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<% if f.object.new_record? %>
<%= f.object._type = params[:_type] %>
<% end %>
<%= f.input :_type, :collection => [['Default Category','Category'],['Product Category','Prodcategory'],['Page Category','Pagecategory']], :include_blank => false %>
<%= f.input :name %>
</div>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
进行了部分修改的 controllers/categories_controller.rb
def update
@category = Category.find(params[:id])
_s = @category.class.to_s.downcase #更改params upload的hash名.
respond_to do |format|
if @category.update_attributes(params[_s]) #upload it!
format.html { redirect_to @category, notice: 'Category was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @category.errors, status: :unprocessable_entity }
end
end
end
在原来的基础上添加了:_type 字段的下拉选框
<%= simple_form_for @category do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<% if f.object.new_record? %>
<%= f.object._type = params[:_type] %>
<% end %>
<%= f.input :_type, :collection => [['Default Category','Category'],['Product Category','Prodcategory'],['Page Category','Pagecategory']], :include_blank => false %>
<%= f.input :name %>
</div>
<%= f.button :submit %>
<% end %>
Done!
举例来说:
这时,通过prodcategories/new
即可直接创建 Prodcategory 的实例。
新建的实例可以通过 Prodcategory 来查找,访问。
如新建 product 时,可以使用这样的 Form:
<%= simple_form_for(@product) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :category_id, :collection => Prodcategory.all, :include_blank => false %>
<%= f.input :name %>
<%= f.input :description %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>