如何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时将Line从Order中取出。 但是,当您编辑订单并删除一行时 ,validation永远不会运行,因为它已从订单中删除。
所以…简而言之,我如何validation与订单关联的行不会更改,并且不会添加或删除?
我想我错过了一些明显的东西。
从ActiveRecord::Associations
的“Association Callbacks”部分,您将看到有几个回调可以添加到has_many
定义中:
-
before_add
-
after_add
-
before_remove
-
after_remove
也来自相同的文档:
如果任何
before_add
回调抛出exception,则该对象不会被添加到集合中。 与before_remove
回调相同; 如果抛出exception,则不会删除该对象。
也许您可以向before_add
和before_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_create
和before_destroy
回调添加到Line
,并检查Order
实例是否已完成。