我正在使用ruby 1.8.7和rails 3.0.3浏览PragProg的Agile Rails开发书
我的ProductsController上的这个destroy方法不会删除我的产品,我不明白为什么。
这是我的第一次切入,我希望“正常工作”
def destroy
@product = Product.find(params[:id])
@product.destroy
respond_to do |format|
format.html { redirect_to(products_url) }
format.xml { head :ok }
end
end
但我断言Product.count的测试失败了。
如果我改为使用这样的删除类方法:
def destroy
Product.delete(params[:id])
respond_to do |format|
format.html { redirect_to(products_url) }
format.xml { head :ok }
end
end
我的测试通过了。
这是测试
test "should destroy product" do
assert_difference('Product.count', -1) do
if @product.referenced_by_line_item
@product.line_items.remove_all
end
delete :destroy, :id => @product.to_param
end
assert_redirected_to products_path
end
我的Product model类上有一个before_destroy方法
def referenced_by_line_item
if line_items.count.zero?
return true
else
errors.add(:base, 'Line Items present')
return false
end
end
任何想法我在这里做错了什么?通过阅读文档(以及在此处搜索其他问题),我希望@ product.destroy和Product.delete(id)能够做同样的事情。
谢谢!
答案 0 :(得分:3)
delete
和destroy
有一个主要区别 - 删除只是删除行,而不调用回调,如http://www.nickpeters.net/2007/12/21/delete-vs-destroy/
在这种情况下,使用delete会绕过您的before_destroy方法,该方法在运行时会向对象添加错误。使用delete时,它会忽略错误,从而更改计数。
答案 1 :(得分:3)
在执行任何操作之前,您正在调用referenced_by_line_item。如果有订单项,则返回false,因此您不会删除订单项。我不认为这是你想要的行为。如果您将其更改为:
if !@product.referenced_by_line_item
@product.line_items.remove_all
end
delete :destroy, :id => @product.to_param
然后@product.destroy
可能会有用。
尽管在referenced_by_line_item
中反转逻辑可能更有意义。更有意义的是,如果有与此产品关联的订单项,则此订单会引用此产品。我认为你的逻辑是倒退的。
此外,布尔方法应以'?'
结尾我看到了这里的复杂情况。如果有行项目,您希望它返回false,以便它不保存,但返回值与方法名称不一致。也许这样的事情会更好:
def no_line_items_present?
if line_items.count > 0
# add errors
return false
else
return true
end
end
然后你可以说:
@product.line_items.remove_all unless @product.no_line_items_present?
delete :destroy, :id => @product.to_param
虽然在决定删除订单项之前我没有看到检查订单项的重点,除了性能略有提升。