如果父记录有子记录,我该如何防止删除父记录?

50

我已经查阅了Ruby on Rails的指南,但似乎无法弄清如何防止删除具有关联子项的父项记录。例如,如果我的数据库中有客户,每个客户可以拥有多个订单,我希望当数据库中存在订单时,阻止删除客户记录。只有没有订单时才能删除客户。

在定义模型之间的关联时,是否有一种方法可以强制执行此行为?

4个回答

117
class Customer < ActiveRecord::Base
  has_many :orders, :dependent => :restrict # raises ActiveRecord::DeleteRestrictionError

编辑:自Rails 4.1起,:restrict选项已不再可用,您应该使用:restrict_with_error:restrict_with_exception中的任一选项。

例如:

class Customer < ActiveRecord::Base
  has_many :orders, :dependent => :restrict_with_error

2
限制信息可以自定义吗? - Cristian Nicoleta
2
请更新此答案以反映Rails 4.1及以上版本的更改:“已删除关联中过时选项:restrict的支持。”http://edgeguides.rubyonrails.org/4_1_release_notes.html。 :dependent选项必须是以下之一:[:destroy, :delete_all, :nullify, :restrict_with_error, :restrict_with_exception]。 - Marklar
嗨,谢谢你。限制错误和异常有什么区别? - BenKoshy
1
restrict_with_error 会为相应的属性添加一个 Rails 错误,而 restrict_with_exception 则会抛出指定的异常。请参阅 https://edgeguides.rubyonrails.org/association_basics.html#options-for-has-one-dependent。 - codener

53

你可以在回调函数中实现这个功能:

class Customer < ActiveRecord::Base
  has_many :orders
  before_destroy :check_for_orders

  private

  def check_for_orders
    if orders.count > 0
      errors.add_to_base("cannot delete customer while orders exist")
      return false
    end
  end
end

编辑

查看这个答案以获得更好的方法。


5
这是最好的方法。它是最清晰的,如果我在处理你的代码,这正是我会寻找这种过滤器的地方。在回调函数中返回“false”可以告诉Rails不要继续执行动作。 - Jaime Bellmyer
3
您可能希望考虑使用Errors#add(:base, msg)。 - Jon Bringhurst
2
我创建了一个小的ActiveRecord插件,适用于需要在各种模型中使用此功能的应用程序:https://github.com/Florent2/prevent_destroy_if_any - Florent2
3
这不是最佳方法。根据@Mauro的回答,一个简单的dependent: :restrict是最好的选择。尽管我怀疑在引入dependent restrict之前,这个答案已经被接受了。 - Damien Roche
3
是的,@Zenph。:dependent => :restrict 后来被添加进来,也是更好的解决方法。 - zetetic
显示剩余4条评论

0

一种可能的方法是在这种情况下避免向用户提供删除链接。

link_to_unless !@customer.orders.empty?

另一种方法是在您的控制器中处理此问题:

if !@customer.orders.empty?
  flash[:notice] = "Cannot delete a customer with orders"
  render :action => :some_action
end

或者,正如Joe所建议的那样,在此处可以使用before_filters,并且可能是更DRY的方法来完成这项工作,尤其是如果您想要将此类型的行为用于不仅仅是Customer的更多模型。


0
尝试在请求处理期间使用过滤器挂钩自定义代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接