Rails 4餐厅订购系统ActiveRecord :: RecordNotFound in ItemsController #create

时间:2015-01-11 16:17:31

标签: ruby-on-rails ruby-on-rails-4 activerecord

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

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

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

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
编辑:我遵循了doon的建议并研究了嵌套资源,这确实是一种更好的方法。更新后的代码如下:

的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>

3 个答案:

答案 0 :(得分:0)

您是否发布到“/ post”或“post / [:id]”?

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

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

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

答案 1 :(得分:0)

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

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

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

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

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

然后在项目控制器的创建操作中,我能够像往常一样使用项目对象中的菜单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

对于如何改进的建议,感觉有点像黑客。

答案 2 :(得分:0)

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

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

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

以下是一些让您指向此方向的更改。

config / routes.rb

 resources :menu do 
    resources :items
 end

现在我们的网址看起来像

 /menu/:menu_id/items/ 

所以我们需要通过查看:menu_id来调整items_controller来获取菜单,我们不再需要隐藏字段了。我将它放在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将生成正确的路径。

<强>菜单/ show.html.erb

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