How can I verify that the has_many relationship is not changed

I have a pretty typical Order model that has_many Lines

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

Once the order is completed, it should not be possible to change any attributes or related lines (although you can change the status that is not completed).

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

This works fine, but if you add, delete, or modify related Lines , it doesn’t interfere.

In my Line model, I have the following check:

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

This successfully stops the rows in the completed order, which is changing, and prevents the row from being added to the completed order.

Therefore, I need to also prevent the removal of rows from a completed order. I tried this in the Line model:

 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 

This works great to prevent the Line from Ordering when changing the Line directly. However, when you edit the Order and delete the Line , the check is not performed because it has already been deleted from the Order .. p>


So ... In short, how can I verify that the lines associated with an order are not changed or added or deleted?

I think I am missing something obvious.

+6
source share
3 answers

In the "Callbacks" section of ActiveRecord::Associations you will see that there are several callbacks that you can add to your has_many definition:

  • before_add
  • after_add
  • before_remove
  • after_remove

Also from the same documents:

If any of the before_add callbacks before_add an exception, the object is not added to the collection. Same thing with before_remove ; if an exception is thrown, the object is not deleted.

Perhaps you can add a callback method to before_add and before_remove , which ensures that the order will not be frozen and throws an exception if it is not allowed.

 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 

Another thing worth trying is to add a validation error and return false within validate_editable_lines! if your validation test fails. If this works, I would recommend changing the method name to validate_editable_lines (sans ! Bang), of course. :)

+3
source

Perhaps add the locked attribute to the model, and after completing the order, set the value locked - true . Then in the controller add before_filter , which will be launched before the update action, so that it checks the value of the locked flag. If it is set to true , then raise the error message / notification / regardless of the fact that this position cannot be changed.

+1
source

This is an interesting problem, and as far as I know, a little difficult to solve.

Here is one approach: http://anti-pattern.com/dirty-associations-with-activerecord

Another approach, which, in my opinion, is a little cleaner, would be to simply check at the controller level before adding / removing Line , rather than using checks.

Another approach is that you can add before_create and before_destroy to Line and check if the Order instance is complete.

0
source

Source: https://habr.com/ru/post/953642/


All Articles