has_many:通过创建子after_save – > ActionView :: Template :: Error

我有三种型号:List,Food和Quantity。 列表和食物通过数量通过has_many:through关联。 模型关联正在做我想要的,但是当我测试时,会出现错误。

test_valid_list_creation_information#ListsCreateTest (1434538267.92s) ActionView::Template::Error: ActionView::Template::Error: Couldn't find Food with 'id'=14 app/views/lists/show.html.erb:11:in `block in _app_views_lists_show_html_erb__3286583530286700438_40342200' app/views/lists/show.html.erb:10:in `_app_views_lists_show_html_erb__3286583530286700438_40342200' test/integration/lists_create_test.rb:17:in `block (2 levels) in ' test/integration/lists_create_test.rb:16:in `block in ' app/views/lists/show.html.erb:11:in `block in _app_views_lists_show_html_erb__3286583530286700438_40342200' app/views/lists/show.html.erb:10:in `_app_views_lists_show_html_erb__3286583530286700438_40342200' test/integration/lists_create_test.rb:17:in `block (2 levels) in ' test/integration/lists_create_test.rb:16:in `block in ' 

我的目标是每次创建一个列表时创建一个新的数量(与该列表相关联)。 每个数量都有amount,food_id和list_id。

  • list_id应该等于刚刚创建的列表的id。
  • food_id应该等于已经存在的随机食物的id。
  • 金额应该是一个随机整数。

在错误中,数字14(“食物与’id’= 14)是通过从1到Food.count中随机选择一个数字生成的.Cood.count等于test / fixtures / foods.yml中的食物对象数量,所以食物肯定是被认可的,至少在我运行Food.count时。那么为什么不存在’id’= 14的食物?

我相信Lists控制器,灯具或集成测试都有问题。 无论导致测试失败的是什么都不会影响性能(一切都在控制台和服务器/用户界面中工作),但我正在尝试理解TDD并编写好的测试,所以我将感谢任何指导。

列表模型:

 class List  :quantities validates :days, presence: true validates :name, uniqueness: { case_sensitive: false } after_save do Quantity.create(food_id: rand(Food.count), list_id: self.id, amount: rand(6)) end end 

数量夹具:

 one: food: grape list: weekend amount: 1 two: food: banana list: weekend amount: 1 

注意:数量夹具之前的组织如下……

 one: food_id: 1 list_id: 1 amount: 1 

……似乎没有任何区别。

lists_create集成测试:

 require 'test_helper' class ListsCreateTest < ActionDispatch::IntegrationTest test "invalid list creation information" do get addlist_path assert_no_difference 'List.count' do post lists_path, list: { days: "a", name: "a" * 141 } end assert_template 'lists/new' end test "valid list creation information" do get addlist_path assert_difference 'List.count', 1 do post_via_redirect lists_path, list: { days: 2, name: "example list" } end assert_template 'lists/show' end end 

并在错误中引用了app / views / lists / show.html.erb:

  

感谢您的任何建议或参考。 如果您需要其他您认为相关的代码或信息,请与我们联系。 我希望使用灯具而不是像FactoryGirl这样的其他方法来完成这一切,即使它意味着一些额外的代码。

Rails 4.2.3,Cloud9。 开发数据库= SQLite3,生产数据库= postgres heroku。

除了在after_save回调中创建一个随机值非常奇怪(我认为你是在做练习,但无论如何最好从一开始就使用好的做法),你永远不应该使用rand(Model.count)来获取样本记录。 有两个主要问题:

  1. rand(upper_bound)方法返回一个介于零和upper_bound参数之间的数字,但不能保证零是第一个创建的id。 我正在使用PostgreSQL,第一个模型的id为1.你可以指定一个范围( rand(1..upper_bound) ),但无论如何你都在赌博当前数据库的工作方式。
  2. 您假设所有记录在任何给定时间按顺序存在,但并非总是如此。 如果删除记录并且随机选择了id,则会出现错误。 该库也可以使用任何策略来创建灯具,因此最好不要假设它是如何工作的。

如果你真的需要随机选择一条记录,我建议你只使用数组的sample方法: Food.all.sample 。 它很慢,但它确实有效。 如果您需要优化,还有其他选择。

现在,我真的建议不惜一切代价避免随机值,只在必要时使用它们。 测试很困难,很难跟踪错误。 此外,我会避免在回调中创建一个关系,它会迅速变成一个无法管理的混乱。

我发布了一个答案,因为在实施建议后,我的错误消失了,我想我对正在发生的事情有了更好的理解。

以前,我在使用关系创建List时在List模型中创建了数量。 现在关系在控制器中,而不是模型中。

没有关系的列表模型:

 class List < ActiveRecord::Base has_many :quantities has_many :foods, :through => :quantities validates :days, presence: true validates :name, uniqueness: { case_sensitive: false } end 

数量夹具和lists_create集成测试未更改。

以前这个show.html.erb包含一个查询。 现在,它只有@quantities,它在Lists控制器中定义。 查询位于控制器中,而不是视图中。

应用程序/视图/列表/ show.html.erb:

 <% provide(:title, @list.name) %> 
<%= link_to "edit the properties of this list", edit_list_path %>

具有show方法中的查询的List控制器(用于过滤具有正确list_id的数量)和create方法中的关系(用于在创建列表时创建新数量)。

 class ListsController < ApplicationController def show @list = List.find(params[:id]) @quantities = [] Quantity.where(:list_id => @list.id).each do |f| @quantities.push("#{f.amount} #{Food.find(f.food_id).name}") end end # ... def create @list = List.new(list_params) if @list.save flash[:success] = "A list has been created!" @a = Food.all.sample.id @b = Food.all.sample.id Quantity.create(food_id: @a, list_id: @list.id, amount: rand(6)) if (@a != @b) Quantity.create(food_id: @b, list_id: @list.id, amount: rand(6)) end redirect_to @list else render 'new' end end # ... end 

如果我理解正确,我会滥用模型和视图,并且不恰当地使用rand和Food.count。

如果您认为我错过了任何内容,或者您​​可以推荐任何改进我的代码的内容,请告诉我。 感谢@mrodrigues,@ joathan和@vamsi的帮助!