如果验证失败,如何呈现视图? (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 %>

其余代码保持不变。