如果验证失败,如何呈现视图? (nil:NilClass 的未定义方法“id”)
How to render view if validation failed? (undefined method `id' for nil:NilClass)
开发者!
我有:
我有 link 一个新的动作盘控制器,传递参数 day_id。看起来像这样:
<%= link_to "Add new dish", new_dish_path(id: @day) %>
我有控制器(菜控)
class DishesController < ApplicationController
def new
@dish = Dish.new
@dish.courses.build
@day = Day.find(params[:id])
end
def create
@dish = Dish.new(dish_params)
if @dish.save
redirect_to '/dashboard'
else
render :new
end
end
private def dish_params
params.require(:dish).permit(:id, :name, :description, :price, :course_name, courses_attributes: [:day_id])
end
end
我有一个看法:
<div class="new dish">
<%= form_for @dish do |d| %>
<% if @dish.errors.any? %>
<% @dish.errors.full_messages.each do |msg| %>
<div class="alert alert-danger"><%= msg %></div>
<% end %>
<% end %>
<%= d.text_field :name, :placeholder => "Dish name" %>
<%= d.text_area :description, :placeholder => "Description" %>
<%= d.text_field :price, :placeholder => "Dish price" %>
<%= d.text_field :course_name, :placeholder => "Course name" %>
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: @day.id %>
<%= d.submit "Save" %>
</div>
<% end %>
<% end %>
当我单击 link“添加新菜”时,url 看起来像这样(id=1 – 这是日期 ID,它可能会根据我从哪一天单击 link):
http://localhost:3000/dishes/new?id=1
如果我填写表格中的所有字段(见上图)- 一切都会好起来的。一道新菜,一道新菜。
问题是什么?
我为模型 Dish 中的 1 个字段添加了简单验证:
class Dish < ApplicationRecord
has_many :courses
has_many :days, through: :courses
has_many :order_items
validates :course_name, presence: true
accepts_nested_attributes_for :courses
end
现在,当我不填写字段并单击“保存”而不是在视图中显示验证错误时,我会收到如下错误:(log)
Started POST "/dishes" for ::1 at 2020-10-23 13:31:43 +0300
(0.1ms) SELECT sqlite_version(*)
Processing by DishesController#create as HTML
Parameters: {"authenticity_token"=>"HfFhXKjv3ldt7vpL3HNDO/udECSItssnMT4tWf9SXaRyejW6+Jd35Vci16ZTSE1ZM0D+0Eain6CJ1DhOZxPgXA==", "dish"=>{"name"=>"", "description"=>"", "price"=>"", "course_name"=>"", "courses_attributes"=>{"0"=>{"day_id"=>"1"}}}, "commit"=>"Save"}
(0.1ms) begin transaction
↳ app/controllers/dishes_controller.rb:11:in `create'
Day Load (0.5ms) SELECT "days".* FROM "days" WHERE "days"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/dishes_controller.rb:11:in `create'
(0.1ms) rollback transaction
↳ app/controllers/dishes_controller.rb:11:in `create'
Rendering dishes/new.html.erb within layouts/application
Rendered dishes/new.html.erb within layouts/application (Duration: 2.0ms | Allocations: 1765)
Completed 500 Internal Server Error in 10ms (ActiveRecord: 0.7ms | Allocations: 6650)
ActionView::Template::Error (undefined method `id' for nil:NilClass):
10: <%= d.text_field :price, :placeholder => "Dish price" %>
11: <%= d.text_field :course_name, :placeholder => "Course name" %>
12: <%= d.fields_for :courses do |c| %>
13: <%= c.hidden_field :day_id, value: @day.id %>
14: <%= d.submit "Save" %>
15: </div>
16: <% end %>
app/views/dishes/new.html.erb:13
app/views/dishes/new.html.erb:12
app/views/dishes/new.html.erb:2
app/controllers/dishes_controller.rb:14:in `create'
我认为发生错误是因为在验证失败时尝试呈现视图时丢失了当天的 ID(请参阅创建控制器操作)。你可以帮助我并告诉我如何解决此行为或告诉我如何实施此行为的最佳做法吗?
您在 new
中所做的视图所需的所有设置也应在 render :new
之前完成。 render :new
立即呈现视图而不调用 new
,这是预期的,因为您不希望 @dish = Dish.new
覆盖您的 @dish
并出现所有错误,对吗?
要么复制设置,将其移至共享方法并调用它,要么使您的视图减少对控制器的依赖。
def create
@dish = Dish.new(dish_params)
if @dish.save
redirect_to '/dashboard'
else
@dish.courses.build
# if not already built by new/save
@day = Day.find(params[:id])
render :new
end
end
对于您的具体情况,您还可以将 @day = ...
移动到 before_action
。
引入全局变量解决了问题
在 DishesController 中而不是:
@day = Day.find(params[:id])
我将变量更改为全局变量:
$day = Day.find(params[:id])
然后在视图中而不是:
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: @day.id %>
我也把变量改成了全局的:
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: $day.id %>
其余代码保持不变。
开发者!
我有:
我有 link 一个新的动作盘控制器,传递参数 day_id。看起来像这样:
<%= link_to "Add new dish", new_dish_path(id: @day) %>
我有控制器(菜控)
class DishesController < ApplicationController
def new
@dish = Dish.new
@dish.courses.build
@day = Day.find(params[:id])
end
def create
@dish = Dish.new(dish_params)
if @dish.save
redirect_to '/dashboard'
else
render :new
end
end
private def dish_params
params.require(:dish).permit(:id, :name, :description, :price, :course_name, courses_attributes: [:day_id])
end
end
我有一个看法:
<div class="new dish">
<%= form_for @dish do |d| %>
<% if @dish.errors.any? %>
<% @dish.errors.full_messages.each do |msg| %>
<div class="alert alert-danger"><%= msg %></div>
<% end %>
<% end %>
<%= d.text_field :name, :placeholder => "Dish name" %>
<%= d.text_area :description, :placeholder => "Description" %>
<%= d.text_field :price, :placeholder => "Dish price" %>
<%= d.text_field :course_name, :placeholder => "Course name" %>
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: @day.id %>
<%= d.submit "Save" %>
</div>
<% end %>
<% end %>
当我单击 link“添加新菜”时,url 看起来像这样(id=1 – 这是日期 ID,它可能会根据我从哪一天单击 link):
http://localhost:3000/dishes/new?id=1
如果我填写表格中的所有字段(见上图)- 一切都会好起来的。一道新菜,一道新菜。
问题是什么?
我为模型 Dish 中的 1 个字段添加了简单验证:
class Dish < ApplicationRecord
has_many :courses
has_many :days, through: :courses
has_many :order_items
validates :course_name, presence: true
accepts_nested_attributes_for :courses
end
现在,当我不填写字段并单击“保存”而不是在视图中显示验证错误时,我会收到如下错误:(log)
Started POST "/dishes" for ::1 at 2020-10-23 13:31:43 +0300
(0.1ms) SELECT sqlite_version(*)
Processing by DishesController#create as HTML
Parameters: {"authenticity_token"=>"HfFhXKjv3ldt7vpL3HNDO/udECSItssnMT4tWf9SXaRyejW6+Jd35Vci16ZTSE1ZM0D+0Eain6CJ1DhOZxPgXA==", "dish"=>{"name"=>"", "description"=>"", "price"=>"", "course_name"=>"", "courses_attributes"=>{"0"=>{"day_id"=>"1"}}}, "commit"=>"Save"}
(0.1ms) begin transaction
↳ app/controllers/dishes_controller.rb:11:in `create'
Day Load (0.5ms) SELECT "days".* FROM "days" WHERE "days"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/controllers/dishes_controller.rb:11:in `create'
(0.1ms) rollback transaction
↳ app/controllers/dishes_controller.rb:11:in `create'
Rendering dishes/new.html.erb within layouts/application
Rendered dishes/new.html.erb within layouts/application (Duration: 2.0ms | Allocations: 1765)
Completed 500 Internal Server Error in 10ms (ActiveRecord: 0.7ms | Allocations: 6650)
ActionView::Template::Error (undefined method `id' for nil:NilClass):
10: <%= d.text_field :price, :placeholder => "Dish price" %>
11: <%= d.text_field :course_name, :placeholder => "Course name" %>
12: <%= d.fields_for :courses do |c| %>
13: <%= c.hidden_field :day_id, value: @day.id %>
14: <%= d.submit "Save" %>
15: </div>
16: <% end %>
app/views/dishes/new.html.erb:13
app/views/dishes/new.html.erb:12
app/views/dishes/new.html.erb:2
app/controllers/dishes_controller.rb:14:in `create'
我认为发生错误是因为在验证失败时尝试呈现视图时丢失了当天的 ID(请参阅创建控制器操作)。你可以帮助我并告诉我如何解决此行为或告诉我如何实施此行为的最佳做法吗?
您在 new
中所做的视图所需的所有设置也应在 render :new
之前完成。 render :new
立即呈现视图而不调用 new
,这是预期的,因为您不希望 @dish = Dish.new
覆盖您的 @dish
并出现所有错误,对吗?
要么复制设置,将其移至共享方法并调用它,要么使您的视图减少对控制器的依赖。
def create
@dish = Dish.new(dish_params)
if @dish.save
redirect_to '/dashboard'
else
@dish.courses.build
# if not already built by new/save
@day = Day.find(params[:id])
render :new
end
end
对于您的具体情况,您还可以将 @day = ...
移动到 before_action
。
引入全局变量解决了问题
在 DishesController 中而不是:
@day = Day.find(params[:id])
我将变量更改为全局变量:
$day = Day.find(params[:id])
然后在视图中而不是:
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: @day.id %>
我也把变量改成了全局的:
<%= d.fields_for :courses do |c| %>
<%= c.hidden_field :day_id, value: $day.id %>
其余代码保持不变。