Rails 4 餐厅订单系统 ActiveRecord::RecordNotFound 在 ItemsController#create

Rails 4 Restaurant order system ActiveRecord::RecordNotFound in ItemsController#create

我正在创建一个简单的餐厅点餐系统,菜单和它的项目之间有一对多的关系。一个菜单有很多项目。为了简单起见,它不是多对多。

我可以很好地创建菜单,但希望能够在菜单显示操作中添加和显示该菜单的菜单项。

菜单显示操作显示正常,但当我尝试添加新菜单项时出现以下错误:

ActiveRecord::RecordNotFound in ItemsController#create
Couldn't find Menu with 'id'=
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"

这是来自终端的查询:

Started POST "/items" for ::1 at 2015-01-11 16:09:44 +0000
Processing by ItemsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"***", "item"=>{"name"=>"Test",     "price"=>"23", "course"=>"Main", "vegetarian"=>"1", "allergy"=>""}, "commit"=>"Add item"}
  Menu Load (0.3ms)  SELECT  `menus`.* FROM `menus` WHERE `menus`.`id` = NULL LIMIT 1
Completed 404 Not Found in 8ms

编辑:我听从了杜恩的建议并研究了嵌套资源,这确实是一种更好的方法。更新后的代码如下:

routes.rb

resources :menus do
  resources :items
end

menus_controller.rb

def show
  @menu = Menu.find(params[:id])
  @items = @menu.items
end

items_controller.rb

def create
  @menu = Menu.find(params[:menu_id])
  @item = @menu.items.create!(item_params)
  if @item.save
    flash[:success] = "Item added!"
    redirect_to @menu
  else
    flash[:danger] = "Errors found!"
    redirect_to @menu
  end
end

private
  def item_params
    params.require(:item).permit(:name, :price, :course, :vegetarian, :allergy, :menu_id)
  end

还有菜单

show.html.erb

<%= link_to "<< Back", menus_path, data: { confirm: back_message } %>

<h1><%= @menu.name %> menu</h1>

<center><button id="toggleButton" class="btn btn-sm btn-info">Show/Hide Add Item Form</button></center>
<br>

<div class="row">

  <div class="col-xs-offset-3 col-xs-6 toggleDiv hideDiv">
    <%= form_for [@menu, Item.new] do |f| %>
      <table class="table table-condensed table-no-border">
        <tr>
          <th scope="row" class="col-xs-2">Name:</th>
          <td class="col-xs-10"><%= f.text_field :name %></td>
        </tr>
        <tr>
          <th scope="row">Price:</th>
          <td><%= f.text_field :price %></td>
        </tr>
        <tr>
          <th scope="row">Course:</th>
          <td><%= f.select(:course, options_for_select([['Starter', 'Starter'], ['Main', 'Main'], ['Dessert', 'Dessert'], ['Drink', 'Drink']]), prompt:     "Please select...", class: 'form-control') %></td>
        </tr>
        <tr>
          <th scope="row">Vegetarian:</th>
          <td><%= f.check_box :vegetarian %></td>
        </tr>
        <tr>
          <th scope="row">Allergy:</th>
          <td><%= f.text_field :allergy %></td>
        </tr>
        <tr><td colspan="2"><%= f.submit "Add item", class: "btn btn-sm btn-success col-xs-offset-4 col-xs-4" %></td></tr>
      </table>
    <% end %>
  </div>
</div>

<table class="table table-condensed">
  <thead>
    <tr>
      <th>Name</th>
      <th>Price</th>
      <th>Course</th>
      <th>Vegetarian</th>
      <th>Allergy</th>
    </tr>
  </thead>
  <tbody>
    <% @items.each do |item| %>
      <tr>
        <td><%= item.name %></td>
        <td><%= number_to_currency(item.price, unit: "£") %></td>
        <td><%= item.course %></td>
        <td><%= item.vegetarian %></td>
        <td><%= item.allergy %></td>
      </tr>
    <% end %>
  </tbody>
</table>

您 post 是要“/post”还是 "post/[:id]"?

@menu = Menu.find(params[:id])

如果不传递 id,将找不到任何东西。 id 可以来自 URL 参数,但这意味着您应该将请求发送到 "/post/[:menuid].

您可以使用 gem https://github.com/charliesome/better_errors 在控制器级别调试您的程序并在那里执行 rails 控制台。只需在你的控制器中输入 "fail" 然后你就会有一个命令行界面,你可以在其中检查你的参数值并确保它包含 id 并在那里玩控制台。

我现在已经解决了。感谢 better_errors 我能够更好地理解发生了什么。

我在 menus_controller.rb 中实现了 aaron 的表演动作。

def show
  @item = Item.new
  @menu = Menu.includes(:items).find(params[:id])
end

我需要一种将菜单 ID 传递到 menu_id 字段的方法,因此我在传递当前 @[=27= 的 table 或 show.html.erb 中添加了一个隐藏字段] 进入 :menu_id 字段。 <%= f.hidden_field :menu_id, :value => @menu.id %>

我在网上了解到,通过隐藏字段传递值并不是一个好主意。

然后在 Items Controller 的创建操作中,我能够像往常一样使用项目对象中的菜单 ID 重定向到现有的显示视图来创建新记录。

def create
  @item = Item.new(item_params)
  if @item.save
    redirect_to menu_path(@item.menu_id)
  else
    redirect_to menu_path(@menu.menu_id)
  end
end

感觉有点像黑客,所以愿意接受有关如何改进的建议。

在这种情况下(特别是如果您的项目不存在于菜单之外)我可能会做的是使用嵌套资源:

http://guides.rubyonrails.org/routing.html#nested-resources

http://railscasts.com/episodes/139-nested-resources(有点过时,但仍然是一个不错的基础)

以下是一些可以帮助您朝这个方向指出的更改。

config/routes.rb

 resources :menu do 
    resources :items
 end

现在我们的 url 看起来像

 /menu/:menu_id/items/ 

所以我们需要调整items_controller通过查看:menu_id来获取菜单,我们不再需要隐藏字段了。我把它放在 before_action 中,因为控制器中的每个方法都将通过关联构建。

items_controller.rb

 class ItemsController < ApplicationController
   before_action :find_menu 

   ...

   def create
      @item = @menu.items.new(item_params)
      if @item.save
        redirect_to @menu, notice: 'item added'
      else
        redirect_to @menu, warning: 'item failed'
      end
   end

   ...
   private

   def find_menu
     @menu = Menu.find(params[:menu_id])
   end
 end

如果你想在菜单上显示它,我们需要一个新的项目来显示。

menus_controller.rb

   def show
     @menu = Menu.find(params[:id])
     @item = @menu.items.new  
   end 

然后我们需要使用 menus/show 视图中的嵌套资源。通过传入菜单和项目的数组,rails 将生成正确的路径。

menus/show.html.erb

 <%= form_for [@menu,@item]  do |f| %>