使用多态关联的 Rails 方法是什么?
What is the Rails way to work with polymorphic associations?
我的 Rails 应用程序中有几个模型,它们是:
- 用户
- 照片
- 专辑
- 评论
我需要发表评论属于 Photo
或 Album
,显然总是属于 User
。为此,我将使用 polymorphic associations。
# models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
问题是,Rails 描述新评论的 #create
动作的方法是什么。我看到了两个选项。
1.描述每个控制器中的注释创建
但这不是 DRY 解决方案。我可以制作一个通用的局部视图来显示和创建评论,但我将不得不重复自己为每个控制器编写评论逻辑。所以它不起作用
2。创建新的 CommentsController
这是我猜的正确方法,但据我所知:
To make this work, you need to declare both a foreign key column and a
type column in the model that declares the polymorphic interface
像这样:
# 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
的最佳方法是什么?
你会是最好的 nesting it in the routes,然后从家长那里委派 class:
# 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| %>
...
我会使用嵌套路由和良好的旧继承。
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
查看参数以确定 "commentable" 资源是什么,但我更喜欢此解决方案,因为 CommentsController
否则最终处理的不仅仅是单一资源,可以膨胀成神 class。
当您有索引操作并需要根据父资源执行连接时,或者当事情变得复杂时,这会非常有用。
使用 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 %>
我的 Rails 应用程序中有几个模型,它们是:
- 用户
- 照片
- 专辑
- 评论
我需要发表评论属于 Photo
或 Album
,显然总是属于 User
。为此,我将使用 polymorphic associations。
# models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
问题是,Rails 描述新评论的 #create
动作的方法是什么。我看到了两个选项。
1.描述每个控制器中的注释创建
但这不是 DRY 解决方案。我可以制作一个通用的局部视图来显示和创建评论,但我将不得不重复自己为每个控制器编写评论逻辑。所以它不起作用
2。创建新的 CommentsController
这是我猜的正确方法,但据我所知:
To make this work, you need to declare both a foreign key column and a type column in the model that declares the polymorphic interface
像这样:
# 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
的最佳方法是什么?
你会是最好的 nesting it in the routes,然后从家长那里委派 class:
# 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| %>
...
我会使用嵌套路由和良好的旧继承。
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
查看参数以确定 "commentable" 资源是什么,但我更喜欢此解决方案,因为 CommentsController
否则最终处理的不仅仅是单一资源,可以膨胀成神 class。
当您有索引操作并需要根据父资源执行连接时,或者当事情变得复杂时,这会非常有用。
使用 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 %>