多态关联(polymorphic associations)算是个挖了很久的坑,一直没填,这次填了这个坑。
是什么?
Polymorphic Associations help when one table must belongs_to multiple other tables。
大白话可以这样理解:当存在多个model 同时 has_many 一个model时,可以使用多态关联,来实现model层面的DRY。【我是这样理解的……】
官方给到的图解案例是这样滴:
Employee和Product同时拥有多个pictures,Picture里面必须包含两个字段:imageable_id,imageable_type。这样就不需要新增EmployeePicture和ProductPicture两个model了,只需要一个Picture就可以了。
这么说好像没啥感觉,通过一个小例子来实战一下。
怎么玩?
新建一个rails项目 poly, 有article,event和comment,用户可以对article或者event进行评论,单个article,event可以拥有多个comments。
rails new poly cd poly rails g model article title:string content:text rails g model event title:string start_at:datetime end_at:datetime description:text rake db:migrate
记得重启服务器:
rails s
。建controller:
rails g controller articles rails g controller events
完成ArticlesController,EventsController中的7个action,这里就不一一列出了。
修改
routes.rb
:Rails.application.routes.draw do resources :events resources :articles root "articles#index" end
使用seeds或者task,生成一些测试数据,这里我用了seeds。
添加articles和events的index,show页面。
浏览器打开
localhost:3000
,类似这样:现在请上comment。
建model comment
运行:
rails g model comment
修改生成的migrate文件如下:
class CreateComments < ActiveRecord::Migration[5.1] def change create_table :comments do |t| t.text :content t.belongs_to :commentable, polymorphic: true t.timestamps end add_index :comments, [:commentable_id, :commentable_type] end end
这里
t.belongs_to :commentable, polymorphic: true
等同与t.integer :commentable_id t.string :commentable_type
运行
rake db:migrate
, 重启服务器。
给article,event,comment建立联系:
修改
app/models/article.rb
,app/models/event.rb
,添加上:has_many :comments, as: :commentable
好,我们去rails console 看看是不是可以给article和event添加comments。
可以看到成功为article创建了comment。
修改
app/controllers/articles_controller.rb
的show部分:def show @article = Article.find(params[:id]) @comments = @article.comments end
修改
app/views/articles/show.html.erb
<div class="container-fluid"> <h1><%= @article.title %></h1> <p><%= @article.content %></p> <ul> <% if @comments %> <% @comments.each do |comment| %> <li><%= comment.content %></li> <% end %> <% end %> </ul> </div>
打开
http://localhost:3000/articles/0
,可以看到刚刚添加的那个comment:DRY comment view
新建comments controller:
rails g controller comments
修改
app/controllers/comments_controller.rb
, 内容如下:class CommentsController < ApplicationController before_action :load_commentable def index @comments = @commentable.comments end def new @comment = @commentable.comments.new end def create @comment = @commentable.comments.new(comment_params) if @comment.save redirect_to @commentable else render :new end end private def load_commentable resource, id = request.path.split('/')[1,2] @commetable = resource.singularize.classify.constantize.find(id) end def comment_params params.require(:comment).permit(:content) end end
修改
routes.rb
:Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html resources :events do resources :comments end resources :articles do resources :comments end root "articles#index" end
新建
app/views/comments/_form.html.erb
:内容如下:
<%= form_for [@commentable, @comment] do |f| %> <div class="field"> <%= f.text_area :content %> </div> <div class="action"> <%= f.submit "sumbit" %> </div> <% end %>
修改
app/controllers/articles_controller.rb
的show部分:def show @commentable = Article.find(params[:id]) @comments = @commentable.comments @comment = @commentable.comments.new end
修改
app/views/articles/show.html.erb
:<div class="container-fluid"> <h1><%= @article.title %></h1> <p><%= @article.content %></p> <ul> <% if @comments %> <% @comments.each do |comment| %> <li><%= comment.content %></li> <% end %> <% end %> </ul> <%= render "comments/form" %> </div>
针对event做同样的处理,这样create comment就可以重复使用了。
看看events:
OK!! 基本功能实现,想要美观地码楼的话,就要用到Ajax部分了,继续写就没完没了了 ,下次在码字: P