2015-08-21 32 views
4

考虑:在Rails 4中验证循环关联的正确方法是什么?

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

我想创建一个验证,以确保一个节点不能被它自己的父母,或者是父母的父母等

我得到这个:

# 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's 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 

它完全有效!实际上这是问题。它是否有效?当Rails调用assign_attributes方法时,我得到了422,但它没有我的验证!相反,我得到一个丑陋的HTML验证错误是这样的:

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

所以,如果Rails可以救不了它的相关记录的Rails返回它自己的错误(一个在上面的代码块),但如果我的记录是相关联本身,我遇到了很大的问题。即使我阻止Node验证其相关的孩子/父母,我仍然会遇到错误。

只要我试图自救记录有错误,那么Rails的替换我422上面的错误。那简直太糟糕了。我想要一个JSON响应错误,以便我的客户知道究竟出了什么问题。

我很难相信没有其他人遇到这个问题,我失去的东西吗?

+0

我发现它有点难以跟随你说在过去的3段什么,因为您使用代词“这”指很多不同的东西。自定义验证器的逻辑看起来对我来说很好,这取决于你所说的你想做什么。如果您的记录与自身相关联,那么为什么会出现错误?你说这应该会发生,对吧?或者,您的问题仅仅是您希望验证者的错误消息显示在日志中,而不是实际获得的错误消息? –

+0

对不起,晚上的时间已经很晚了,我想我在那时开始讨厌代词。希望这个问题现在更有意义。 问题出在什么错误,我想从我的验证中得到的错误,而不是Rails的通用ActiveRecord :: RecordNotSaved错误。防止关联被持久化是一回事,但是让记录返回我自己的错误是另一回事:“节点不能是它自己的父母”。 – Harbinger

+0

哦,所以你不能在你的控制器中添加一个条件,如果创建失败,它会调用模型中的errors方法并返回JSON响应?通过这种方式,您明确告诉Rails您希望JSON响应消息是什么?无论如何,Rails可能会感到困惑,因为如果记录引用自身,技术上仍然试图创建子关联,并且它通过验证该子关联失败,因此消息必须是模糊的,因为它是has_many关联,并且即使有多个错误也必须有意义。 –

回答

0

你打电话的Node模型验证?如果是的话,上面的代码应该可以工

我想这一点,

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 

的验证码是相同的。这是在控制台中测试的结果。

Here is the result of the testing in console

+0

是的,我把Validator添加到模型中,仍然没有工作。另外,你确定你的例子正在工作吗?你在两个协会都有外键......这感觉不对,不是? 对我来说,这说的是一个节点有一个父节点,由该父节点的parent_id标识。这没有道理! – Harbinger

+0

是的,你是对的,你可以使用'parent_id'来获取一个'Node'的父节点。数据库中的关系使用'foreign_key'' parent_id'维护。我明确指出它是可读的 默认情况下,Rails使用'resource name'+'_id'(即parent_id来获取父项)。但是在'has_many'关系的情况下,它会查找'node_id'列。所以我们需要将'foreign_key'指定为'parent_id'。 欲了解更多信息,你可以看看'self join'和'为belongs_to协会创建外键'部分在http://guides.rubyonrails.org/association_basics.html – Monika

+0

右,我理解外键,但退房你链接的文档,你的例子毫无疑问是错误的。你不想在两个关系上外交钥匙。想想看,对于Node的孩子关系,你在每个指向父母的孩子身上放置一个id;你不想在你的父母身上放一个身份证,指向孩子,这就是你在上面所做的。我认为这可能与你让你的422有错误有关,但你明白为什么这是不好的? – Harbinger

相关问题