Rails 处理多态关联的方式是什么?

问题描述 投票:0回答:2

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

  1. 用户
  2. 照片
  3. 相册
  4. 评论

我需要发表属于

Photo
Album
的评论,并且显然总是属于
User
。我将为此使用多态关联

# models/comment.rb

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

问题是,Rails 如何描述新评论的

#create
操作。我看到有两种选择。

1。描述每个控制器中的注释创建

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

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_id
commetable_type
?我猜,
commentable_type
可能是一个型号名称。

另外,从其他视图制作

form_for @comment
的最佳方法是什么?

ruby-on-rails ruby ruby-on-rails-4 activerecord polymorphic-associations
2个回答
8
投票

我会将嵌套路由与良好的旧继承一起使用。

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

  private

  def set_commentable
    @commentable = commentable_class.find(params[:id])
  end

  def commentable_class
    raise("commentable_class must be implemented by subclasses")
  end
  # ...
end

module Albums
  class CommentsController < ::CommentsController
    private
    def commentable_class
      Album
    end
  end
end

module Photos
  class CommentsController < ::CommentsController
  private
  def commentable_class
    Photo
  end
end

虽然您可以简单地让

CommentsController
查看参数来确定什么是“可注释”资源,但我更喜欢这个解决方案,因为
CommentsController
否则最终会处理远远不止一个资源,并且可能会膨胀为上帝类。

当您有索引操作并需要基于父资源执行联接或当事情变得复杂时,这确实非常有用。

使用部分内容制作可重复使用的表单非常简单:

# 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 %>

5
投票

你最好将其嵌套在路由中,然后从父类中进行委托:

# 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| %>
  ...
© www.soinside.com 2019 - 2024. All rights reserved.