【问题标题】:What is the Rails way to work with polymorphic associations?Rails 使用多态关联的方式是什么?
【发布时间】:2016-01-27 12:14:47
【问题描述】:

我的 Rails 应用程序中有几个模型,它们是:

  1. 用户
  2. 照片
  3. 专辑
  4. 评论

我需要让 cmets 隶属于 PhotoAlbum,并且显然始终属于 User。我将为此使用polymorphic associations

# models/comment.rb

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

问题是,Rails 描述新评论的#create 动作的方式是什么。我看到了两种选择。

1.描述每个控制器中的评论创建

但这不是一个 DRY 解决方案。我可以制作一个通用的局部视图来显示和创建 cmets,但我将不得不重复自己为每个控制器编写 cmets 逻辑。所以它不起作用

2。创建新的 CommentsController

我猜这是正确的方式,但据我所知:

要完成这项工作,您需要同时声明一个外键列和一个 模型中声明多态接口的类型列

像这样:

# schema.rb

  create_table "comments", force: :cascade do |t|
    t.text     "body"
    t.integer  "user_id"
    t.integer  "commentable_id"
    t.string   "commentable_type"
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
  end

所以,当我将编写非常简单的控制器时,它将接受来自远程表单的请求:

# controllers/comments_controller.rb

class CommentsController < ApplicationController
  def new
    @comment = Comment.new
  end

  def create
    @commentable = ??? # How do I get commentable id and type?
    if @comment.save(comment_params)
      respond_to do |format|
        format.js {render js: nil, status: :ok}
      end
    end
  end

  private

  def comment_params
    defaults = {:user_id => current_user.id, 
                :commentable_id => @commentable.id, 
                :commentable_type => @commentable.type}
    params.require(:comment).permit(:body, :user_id, :commentable_id, 
                                    :commentable_type).merge(defaults)
  end
end

如何获得commentable_idcommetable_type?我猜,commentable_type 可能是型号名称。

另外,从其他视图制作form_for @comment 的最佳方法是什么?

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 activerecord polymorphic-associations


    【解决方案1】:

    我会使用嵌套路由和良好的旧继承。

    Rails.application.routes.draw do
    
    
      resources :comments, only: [:show, :edit, :update, :destroy] # chances are that you don't need all these actions...
    
      resources :album, shallow: true do
        resources :comments, only: [:new, :index, :create],  module: 'albums'
      end
    
      resources :photos, shallow: true do
        resources :comments, only: [:new, :index, :create],  module: 'photos'
      end
    end
    

    class CommentsController < ApplicationController
      before_action :set_commentable, only: [:new, :index, :create]
    
      def create
         @comment = @commentable.comments.new(comment_params) do |c|
           c.user = current_user
         end
         # ...
      end
    
      # ...
    end
    
    class Albums::CommentsController < ::CommentsController
      private
        def set_commentable
          @commentable = Album.find(param[:id])
        end
    end
    
    class Photos::CommentsController < ::CommentsController
      private
        def set_commentable
          @commentable = Photo.find(param[:id])
        end
    end
    

    虽然您可以简单地让 CommentsController 查看参数以确定“可评论”资源是什么,但我更喜欢这个解决方案,因为 CommentsController 否则最终处理的不仅仅是单一资源,而且可以膨胀成神类。

    当您有索引操作并且需要基于父资源执行连接或事情变得复杂时,这真的很出色。

    使用 partials 制作可重用的表单非常简单:

    # views/comments/_form.html.erb
    <%= form_for([commentable, commentable.comment.new]) do |f| %>
      <%= f.label :body %>
      <%= f.text_field :body %>
    <% end %>
    

    然后你可以像这样包含它:

    <%= render partial: 'comments/form', commentable: @album %>
    

    【讨论】:

      【解决方案2】:

      你最好nesting it in the routes,然后从父类委派:

      # config/routes.rb
      resources :photos, :albums do
         resources :comments, only: :create #-> url.com/photos/:photo_id/comments
      end
      
      # app/controllers/comments_controller.rb
      class CommentsController < ApplicationController
         def create
            @parent  = parent
            @comment = @parent.comments.new comment_params
            @comment.save
         end
      
         private
      
         def parent
            return Album.find params[:album_id] if params[:album_id]
            Photo.find params[:photo_id] if params[:photo_id]
         end
      
         def comment_params
            params.require(:comment).permit(:body).merge(user_id: current_user.id)
         end
      end
      

      这将自动为您填写。


      为了给自己一个@comment 对象,你必须使用:

      #app/controllers/photos_controller.rb
      class PhotosController < ApplicationController
         def show
            @photo = Photo.find params[:id] 
            @comment = @photo.comments.new
         end
      end
      
      #app/views/photos/show.html.erb
      <%= form_for [@photo, @comment] do |f| %>
        ...
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多