What is the correct way to check for circular associations in Rails 4?

Given:

def Node belongs_to :parent, class_name: :Node has_many :children, class_name: :Node, foreign_key: :parent_id end 

I am trying to create a check to make sure that Node cannot be its own parent or parent, etc.

I got it:

 # Custom validator to check if the Parent Node is related to the current node. Avoids that ugly self-association loop. # class NodeParentValidator < ActiveModel::Validator def validate(node) @node = node unless validate_recursive(node) node.errors[:parent_id] << "Node can't be it own parent" end end def validate_recursive(node) # If the validating node doesn't have a parent, we return true because we're either facing a node that doesn't have any parent # or we got to the point that we need to stop the recursive loop. return true if node.parent.nil? # If the validating node is the same as the current "parent", we found the self-association loop. This is bad. return false if @node.id == node.parent_id # While we don't get to the end of the association, keep jumping. return false unless validate_recursive(node.parent) return true end end 

And it works completely! This is actually a problem. It works. When Rails calls the assign_attributes method, I get 422, but it doesn't have my check! Instead, I get an ugly HTML validation error like this:

 ActiveRecord::RecordNotSaved (Failed to replace children because one or more of the new records could not be saved.) 

So, if Rails cannot save related records, Rails returns its own error (the one that is in the code block above), but if my record is self-related, I get a big problem. Even if I stopped Node from checking related child / parent objects, I still get an error message.

While the record I'm trying to save TSELF has an error, then Rails replaces my 422 with the error above. And this is just bad. I want a JSON response error so that my client knows what exactly went wrong.

It’s hard for me to believe that no one else has encountered this problem, have I missed something?

+4
source share
1 answer

Did you call the validator in the Node model? If so, then the above code should work.

I tried this

 class Node < ActiveRecord::Base has_many :children, class_name: :Node, foreign_key: :parent_id belongs_to :parent, class_name: :Node, foreign_key: :parent_id validates_with NodeParentValidator end 

The validator code is the same. Here is the test result in the console.

Here is the test result in the console

0
source

All Articles