如何validationhas_many关系未更改

我有一个相当典型的Order模型, has_many Lines

 class Order < ActiveRecord::Base has_many :lines validates_associated :lines 

订单完成后,不应该更改任何属性或相关行(尽管您可以将状态更改为未完成)。

  validate do if completed_at.nil? == false && completed_at_was.nil? == false errors.add(:base, "You can't change once complete") end end 

这样可以正常工作, 但是 ,如果添加,删除或更改关联的 ,则不会阻止此操作。

在我的Line模型中,我有以下validation:

 validate do if order && order.completed_at.nil? == false errors.add(:base, "Cannot change once order completed.") end end 

这会成功停止正在修改的已完成订单中的行,并阻止将行添加到已完成的订单中。

因此,我还需要防止线路从已完成的订单中取出。 我在Line模型中试过这个:

 validate do if order_id_was.nil? == false if Order.find(order_id_was).completed_at.nil? == false errors.add(:base, "Cannot change once order completed.") end end end 

这样可以正常工作,以防止在直接修改Line时将LineOrder中取出。 但是,当您编辑订单并删除一行时 ,validation永远不会运行,因为它已从订单中删除。


所以…简而言之,我如何validation与订单关联的不会更改,并且不会添加或删除?

我想我错过了一些明显的东西。

ActiveRecord::Associations的“Association Callbacks”部分,您将看到有几个回调可以添加到has_many定义中:

  • before_add
  • after_add
  • before_remove
  • after_remove

也来自相同的文档:

如果任何before_add回调抛出exception,则该对象不会被添加到集合中。 与before_remove回调相同; 如果抛出exception,则不会删除该对象。

也许您可以向before_addbefore_remove添加一个回调方法,以确保订单不会被冻结,并在不允许的情况下抛出exception。

 has_many :lines, before_add: :validate_editable!, before_remove: :validate_editable! private def validate_editable_lines!(line) # Define the logic of how `editable?` works based on your requirements raise ActiveRecord::RecordNotSaved unless editable?(line) end 

另一件值得尝试的事情是添加validation错误并在validate_editable_lines!返回false validate_editable_lines! 如果您的validation测试失败。 如果可行,我建议将方法名称更改为validate_editable_lines (sans ! bang)。 🙂

可能会向模型添加一个locked属性,并在订单完成后将locked的值设置为true 。 然后,在控制器中,添加将在更新操作之前触发的before_filter ,以便检查locked标志的值。 如果设置为true则向用户提出错误/通知/无论该行项目无法更改。

这是一个有趣的问题,据我所知,解决起来有点棘手。

这是一种方法: http : //anti-pattern.com/dirty-associations-with-activerecord

我认为稍微清洁的另一种方法是在添加/删除Line之前简单地检查控制器级别,而不是使用validation。

另一种方法是您可以将before_createbefore_destroy回调添加到Line ,并检查Order实例是否已完成。