Rails: deep nested resources gives error: undefined method 'comments'
Rails: deep nested resources gives error: undefined method 'comments'
我在 rails 应用程序中收到此错误 undefined method 'comments'
。我知道我不应该做 >1 级嵌套资源,但我不知道如何在这种情况下应用正确的方法。
目前这是我的路线:
resources :performance_indicators do
resources :improvement_actions do
member do
put "like" => "improvement_actions#upvote"
put "unlike" => "improvement_actions#downvote"
end
resources :comments
end
end
正如我所说,我遇到了这个错误:
NoMethodError in PerformanceIndicators#show
Showing .../app/views/comments/_form.html.erb where line #1 raised:
undefined method `comments' for nil:NilClass
不知道是不是控制器的问题。任何人都可以帮忙吗? :)
编辑:
我的comment/_form:
<%= form_for([@performance_indicator, @improvement_action, @improvement_action.comments.build]) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
这是我的 CommentsController:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :set_improvement_action
# GET /comments
# GET /comments.json
def index
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
@comment = @improvement_action.comments.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
@comment = @improvement_action.comments.new(comment_params)
if @comment.save
format.html { redirect_to [@improvement_action.performance_indicator, @improvement_action], notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: [@improvement_action, @comment] }
else
format.html { render :new }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if @comment.update(comment_params)
format.html { redirect_to @comment, notice: 'Comment was successfully updated.' }
format.json { render :show, status: :ok, location: @comment }
else
format.html { render :edit }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
@comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
@comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:body)
end
def set_improvement_action
@improvement_action = ImprovementAction.includes(:comments).find(params[:improvement_action_id])
end
end
这是我的 PerformanceIndicatorController:
class PerformanceIndicatorsController < ApplicationController
before_action :set_performance_indicator, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /performance_indicators
# GET /performance_indicators.json
def index
@performance_indicators = PerformanceIndicator.all
end
# GET /performance_indicators/1
# GET /performance_indicators/1.json
def show
#@performance_indicators = PerformanceIndicator.all.order("created_at DESC")
end
# GET /performance_indicators/new
def new
@performance_indicator = PerformanceIndicator.new
@comments = Comment.new
end
# GET /performance_indicators/1/edit
def edit
end
# POST /performance_indicators
# POST /performance_indicators.json
def create
@performance_indicator = PerformanceIndicator.new(performance_indicator_params)
respond_to do |format|
if @performance_indicator.save
format.html { redirect_to @performance_indicator, notice: 'Performance indicator was successfully created.' }
format.json { render :show, status: :created, location: @performance_indicator }
else
format.html { render :new }
format.json { render json: @performance_indicator.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /performance_indicators/1
# PATCH/PUT /performance_indicators/1.json
def update
respond_to do |format|
if @performance_indicator.update(performance_indicator_params)
format.html { redirect_to @performance_indicator, notice: 'Performance indicator was successfully updated.' }
format.json { render :show, status: :ok, location: @performance_indicator }
else
format.html { render :edit }
format.json { render json: @performance_indicator.errors, status: :unprocessable_entity }
end
end
end
# DELETE /performance_indicators/1
# DELETE /performance_indicators/1.json
def destroy
@performance_indicator.destroy
respond_to do |format|
format.html { redirect_to performance_indicators_url, notice: 'Performance indicator was successfully destroyed.' }
format.json { head :no_content }
end
end
def set_comment
@improvement_action = ImprovementAction.find(params[:improvement_action_id])
@comment = Comment.find(params[:id])
end
private
# Use callbacks to share common setup or constraints between actions.
def set_performance_indicator
@performance_indicator = PerformanceIndicator.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def performance_indicator_params
params.require(:performance_indicator).permit(:name, :numberTimesIdentifiedProblems, :numberTimesAnalysed)
end
end
让我们从修复深层嵌套开始。
resources :performance_indicators, shallow: true do
resources :improvement_actions
end
resources :improvement_actions, only: [] do
member do
put "like" => "improvement_actions#upvote"
put "unlike" => "improvement_actions#downvote"
end
resources :comments
end
only: []
是一个巧妙的技巧,将路由嵌套在资源下但抑制路由的生成。我们希望如此,因为第一个块实际上声明了我们需要的所有路由。
Prefix Verb URI Pattern Controller#Action
performance_indicator_improvement_actions GET /performance_indicators/:performance_indicator_id/improvement_actions(.:format) improvement_actions#index
POST /performance_indicators/:performance_indicator_id/improvement_actions(.:format) improvement_actions#create
new_performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/new(.:format) improvement_actions#new
edit_performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/:id/edit(.:format) improvement_actions#edit
performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#show
PATCH /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#update
PUT /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#update
DELETE /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#destroy
performance_indicators GET /performance_indicators(.:format) performance_indicators#index
POST /performance_indicators(.:format) performance_indicators#create
new_performance_indicator GET /performance_indicators/new(.:format) performance_indicators#new
edit_performance_indicator GET /performance_indicators/:id/edit(.:format) performance_indicators#edit
performance_indicator GET /performance_indicators/:id(.:format) performance_indicators#show
PATCH /performance_indicators/:id(.:format) performance_indicators#update
PUT /performance_indicators/:id(.:format) performance_indicators#update
DELETE /performance_indicators/:id(.:format) performance_indicators#destroy
like_improvement_action PUT /improvement_actions/:id/like(.:format) improvement_actions#upvote
unlike_improvement_action PUT /improvement_actions/:id/unlike(.:format) improvement_actions#downvote
improvement_action_comments GET /improvement_actions/:improvement_action_id/comments(.:format) comments#index
POST /improvement_actions/:improvement_action_id/comments(.:format) comments#create
new_improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/new(.:format) comments#new
edit_improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/:id/edit(.:format) comments#edit
improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#show
PATCH /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#update
PUT /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#update
DELETE /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#destroy
取消嵌套资源意味着需要跳过的环节会少很多。
class CommentsController < ApplicationController
before_action :set_improvement_action
# GET /improvement_actions/:improvement_action_id/comments/new
def new
@comment = @improvement_action.comments.new
end
# POST /improvement_actions/:improvement_action_id/comments
def create
@comment = @improvement_action.comments.new(comment_params) do |c|
# @todo - you should associate comment with the user who created it at some point.
# c.author = current_user
end
# note that you where saving the record twice!
if @comment.save
format.html { redirect_to [@improvement_action.performance_indicator, @improvement_action], notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: [@improvement_action, @comment] }
else
format.html { render :new }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
# ...
private
def comment_params
params.require(:comment).permit(:body)
end
def set_improvement_action
@improvement_action = ImprovementAction.includes(:comments)
.find(params[:improvement_action_id])
end
end
如果可能,您应该在控制器端处理新记录的播种。
<%= form_for([@improvement_action, @comment]) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
我在 rails 应用程序中收到此错误 undefined method 'comments'
。我知道我不应该做 >1 级嵌套资源,但我不知道如何在这种情况下应用正确的方法。
目前这是我的路线:
resources :performance_indicators do
resources :improvement_actions do
member do
put "like" => "improvement_actions#upvote"
put "unlike" => "improvement_actions#downvote"
end
resources :comments
end
end
正如我所说,我遇到了这个错误:
NoMethodError in PerformanceIndicators#show
Showing .../app/views/comments/_form.html.erb where line #1 raised:
undefined method `comments' for nil:NilClass
不知道是不是控制器的问题。任何人都可以帮忙吗? :)
编辑:
我的comment/_form:
<%= form_for([@performance_indicator, @improvement_action, @improvement_action.comments.build]) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
这是我的 CommentsController:
class CommentsController < ApplicationController
before_action :set_comment, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
before_action :set_improvement_action
# GET /comments
# GET /comments.json
def index
end
# GET /comments/1
# GET /comments/1.json
def show
end
# GET /comments/new
def new
@comment = @improvement_action.comments.new
end
# GET /comments/1/edit
def edit
end
# POST /comments
# POST /comments.json
def create
@comment = @improvement_action.comments.new(comment_params)
if @comment.save
format.html { redirect_to [@improvement_action.performance_indicator, @improvement_action], notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: [@improvement_action, @comment] }
else
format.html { render :new }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
# PATCH/PUT /comments/1
# PATCH/PUT /comments/1.json
def update
respond_to do |format|
if @comment.update(comment_params)
format.html { redirect_to @comment, notice: 'Comment was successfully updated.' }
format.json { render :show, status: :ok, location: @comment }
else
format.html { render :edit }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
@comment.destroy
respond_to do |format|
format.html { redirect_to comments_url, notice: 'Comment was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_comment
@comment = Comment.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def comment_params
params.require(:comment).permit(:body)
end
def set_improvement_action
@improvement_action = ImprovementAction.includes(:comments).find(params[:improvement_action_id])
end
end
这是我的 PerformanceIndicatorController:
class PerformanceIndicatorsController < ApplicationController
before_action :set_performance_indicator, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /performance_indicators
# GET /performance_indicators.json
def index
@performance_indicators = PerformanceIndicator.all
end
# GET /performance_indicators/1
# GET /performance_indicators/1.json
def show
#@performance_indicators = PerformanceIndicator.all.order("created_at DESC")
end
# GET /performance_indicators/new
def new
@performance_indicator = PerformanceIndicator.new
@comments = Comment.new
end
# GET /performance_indicators/1/edit
def edit
end
# POST /performance_indicators
# POST /performance_indicators.json
def create
@performance_indicator = PerformanceIndicator.new(performance_indicator_params)
respond_to do |format|
if @performance_indicator.save
format.html { redirect_to @performance_indicator, notice: 'Performance indicator was successfully created.' }
format.json { render :show, status: :created, location: @performance_indicator }
else
format.html { render :new }
format.json { render json: @performance_indicator.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /performance_indicators/1
# PATCH/PUT /performance_indicators/1.json
def update
respond_to do |format|
if @performance_indicator.update(performance_indicator_params)
format.html { redirect_to @performance_indicator, notice: 'Performance indicator was successfully updated.' }
format.json { render :show, status: :ok, location: @performance_indicator }
else
format.html { render :edit }
format.json { render json: @performance_indicator.errors, status: :unprocessable_entity }
end
end
end
# DELETE /performance_indicators/1
# DELETE /performance_indicators/1.json
def destroy
@performance_indicator.destroy
respond_to do |format|
format.html { redirect_to performance_indicators_url, notice: 'Performance indicator was successfully destroyed.' }
format.json { head :no_content }
end
end
def set_comment
@improvement_action = ImprovementAction.find(params[:improvement_action_id])
@comment = Comment.find(params[:id])
end
private
# Use callbacks to share common setup or constraints between actions.
def set_performance_indicator
@performance_indicator = PerformanceIndicator.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def performance_indicator_params
params.require(:performance_indicator).permit(:name, :numberTimesIdentifiedProblems, :numberTimesAnalysed)
end
end
让我们从修复深层嵌套开始。
resources :performance_indicators, shallow: true do
resources :improvement_actions
end
resources :improvement_actions, only: [] do
member do
put "like" => "improvement_actions#upvote"
put "unlike" => "improvement_actions#downvote"
end
resources :comments
end
only: []
是一个巧妙的技巧,将路由嵌套在资源下但抑制路由的生成。我们希望如此,因为第一个块实际上声明了我们需要的所有路由。
Prefix Verb URI Pattern Controller#Action
performance_indicator_improvement_actions GET /performance_indicators/:performance_indicator_id/improvement_actions(.:format) improvement_actions#index
POST /performance_indicators/:performance_indicator_id/improvement_actions(.:format) improvement_actions#create
new_performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/new(.:format) improvement_actions#new
edit_performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/:id/edit(.:format) improvement_actions#edit
performance_indicator_improvement_action GET /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#show
PATCH /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#update
PUT /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#update
DELETE /performance_indicators/:performance_indicator_id/improvement_actions/:id(.:format) improvement_actions#destroy
performance_indicators GET /performance_indicators(.:format) performance_indicators#index
POST /performance_indicators(.:format) performance_indicators#create
new_performance_indicator GET /performance_indicators/new(.:format) performance_indicators#new
edit_performance_indicator GET /performance_indicators/:id/edit(.:format) performance_indicators#edit
performance_indicator GET /performance_indicators/:id(.:format) performance_indicators#show
PATCH /performance_indicators/:id(.:format) performance_indicators#update
PUT /performance_indicators/:id(.:format) performance_indicators#update
DELETE /performance_indicators/:id(.:format) performance_indicators#destroy
like_improvement_action PUT /improvement_actions/:id/like(.:format) improvement_actions#upvote
unlike_improvement_action PUT /improvement_actions/:id/unlike(.:format) improvement_actions#downvote
improvement_action_comments GET /improvement_actions/:improvement_action_id/comments(.:format) comments#index
POST /improvement_actions/:improvement_action_id/comments(.:format) comments#create
new_improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/new(.:format) comments#new
edit_improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/:id/edit(.:format) comments#edit
improvement_action_comment GET /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#show
PATCH /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#update
PUT /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#update
DELETE /improvement_actions/:improvement_action_id/comments/:id(.:format) comments#destroy
取消嵌套资源意味着需要跳过的环节会少很多。
class CommentsController < ApplicationController
before_action :set_improvement_action
# GET /improvement_actions/:improvement_action_id/comments/new
def new
@comment = @improvement_action.comments.new
end
# POST /improvement_actions/:improvement_action_id/comments
def create
@comment = @improvement_action.comments.new(comment_params) do |c|
# @todo - you should associate comment with the user who created it at some point.
# c.author = current_user
end
# note that you where saving the record twice!
if @comment.save
format.html { redirect_to [@improvement_action.performance_indicator, @improvement_action], notice: 'Comment was successfully created.' }
format.json { render :show, status: :created, location: [@improvement_action, @comment] }
else
format.html { render :new }
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
# ...
private
def comment_params
params.require(:comment).permit(:body)
end
def set_improvement_action
@improvement_action = ImprovementAction.includes(:comments)
.find(params[:improvement_action_id])
end
end
如果可能,您应该在控制器端处理新记录的播种。
<%= form_for([@improvement_action, @comment]) do |f| %>
<div class="field">
<%= f.label :body %><br>
<%= f.text_area :body %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>