Validation 如何避免在自引用模型中由于不引用自身和不重复引用而更新记录

Validation 如何避免在自引用模型中由于不引用自身和不重复引用而更新记录,validation,ruby-on-rails-4,model,self-reference,rails-models,Validation,Ruby On Rails 4,Model,Self Reference,Rails Models,我有自参考模型: class Component < ActiveRecord::Base has_and_belongs_to_many :allergens has_many :cqnames, as: :cqnable has_many :inclusions has_many :ingredients, :through => :inclusions accepts_nested_attributes_for :cqnames, allow_destroy

我有自参考模型:

class Component < ActiveRecord::Base
  has_and_belongs_to_many :allergens
  has_many :cqnames, as: :cqnable
  has_many :inclusions
  has_many :ingredients, :through => :inclusions
  accepts_nested_attributes_for :cqnames, allow_destroy: true
  accepts_nested_attributes_for :ingredients
  accepts_nested_attributes_for :inclusions, allow_destroy: true
  translates :name, :description
  validates :name, presence: true, uniqueness: true


  def self.get_children(ing)
    @tree ||= []
    if ing.ingredients.nil?
      return @tree
    else
      @tree << ing.ingredients
      get_children(ing.ingredients)
    end
  end
end
我必须避免自引用同一记录,也要避免在更新相关记录时引用已引用的记录

在验证之前进行试验…:

class Component < ActiveRecord::Base
  before_validation :hokus
  has_and_belongs_to_many :allergens
  has_many :cqnames, as: :cqnable
  has_many :inclusions
  has_many :ingredients, :through => :inclusions
  has_many :inverse_inclusions, :class_name => "Inclusion", :foreign_key => "ingredient_id"
  has_many :composites, :through => :inverse_inclusions, :source => :component
  accepts_nested_attributes_for :cqnames, allow_destroy: true
  accepts_nested_attributes_for :ingredients
  accepts_nested_attributes_for :inclusions, allow_destroy: true
  translates :name, :description
  validates :name, presence: true, uniqueness: true


  require 'set'
  def self.pokus(ing)
    avoid = Set.new([self])
    q = true
    if ing.ingredients.present?
      ing.ingredients.each do |record|
        q = false if avoid.include? record
      end
    end
    q
  end

  def hokus
    Component.pokus(self)
  end

end

我觉得这有点乱。但我会给你一个指针,告诉你如何建立一个没有重复项的列表

require 'set'
def self.get_children(ing)
  tree = Set.new
  avoid = Set.new([self])

  recursive = lambda {|recs|
    recs.ingredients.each do |record|
      pass = false
      if avoid.include? record
        pass = true
      else
        tree << record
        avoid << record
      end
      recursive.call(record.ingredients) unless pass
    end
  }
  recursive.call(ing.ingredients)
  tree.to_a
end
我已经做了一些类似这样的递归工作,所以我相信这会实现您希望的。虽然我还没有测试这段代码


如果你想看看我写的深度优先记录复制方法,你可以在我的gem中看到。我在我构建的集合上使用了一个变体,它与我在这里演示的避免集合基本相同。我在我的博客文章中解释了这一点,如果你想要一个更简单的版本来完成这项工作,那么这就可以了

require 'set'
def self.get_children(ing)
  tree = Set.new

  recursive = lambda {|recs|
    recs.ingredients.each do |record|
      recursive.call(record.ingredients) if tree.add?(record)
    end       
  }

  recursive.call(ing.ingredients)

  tree.to_a
end

非常感谢你的回复,丹。我是一个绝对的编码新手,我无法想象你的代码会做什么。我研究了很多,包括你的博客文章和PBT。通常,在我的自引用模型类中,我必须避免任何深度的循环引用。我可以想象算法-BMO适当的逻辑可以在保存/更新之前在模型级别实现,或者在控制器表单级别实现,以避免在相关的选择框中提供不适当的选择,可能两者都有,但我的编码技能仍然非常差,我不知道如何编码它…Set是一个独特的列表。它类似于数组,但只保留每个对象中的一个。因此,它将为您完成大部分工作。你设计东西的方式看起来很难维护。如果你能避免的话,我会避免使用has_和would_to_many。你能保持的越简单,你的生活就会越好。你要做的不是一个新手。你可能有兴趣研究一个现有的gem,它将为你提供层次结构方面的东西。这个自引用模型实际上是-有很多:成分,:通过=>:内含物-而不是HABTM。这不是毫无目的的设计。所有这些协会都运作良好。我知道这有点复杂。我已经找到了,但BMO不是我要找的。我需要的正是自我参照所拥有的:通过联想,正如我所写的,它非常有效。我所要做的就是避免这种循环的自我参照。但这超出了我目前的技能。多年来,我一直在准备和分析我们应用程序的设计,但没有编码。在过去的几年里,我通常在Drupal6/7上构建它,这也是一个复杂的平台,但它有很多限制,我决定通过学习编写RubyRails应用程序来打破这些限制。如果您熟悉c9.io IDE,我可以授予您访问我的dev env的权限,以便更好地了解我的工作内容。在irb/rails控制台中对各个部分进行实验,看看它们是如何工作的。集合。添加?方法是您想要的关键。看到我的另一个答案了吗。