代码之家  ›  专栏  ›  技术社区  ›  Anurag

A的Rails验证有多个关联

  •  4
  • Anurag  · 技术社区  · 15 年前

    在子对象存在的情况下,验证has-many关系时遇到问题,但父对象不存在。但是,在创建/保存父对象时,我希望确保已保存特定的子对象(具有某些属性)。

    有一个 Parent 对象 has_many Child 物体。这个 孩子 对象首先被持久化到数据库中,因此没有任何对父对象的引用。关联结构为:

    Parent
      - has_many :children 
    
    Child
      - someProperty: string
      - belongs_to: parent
    

    例如,有三个子对象:

    #1 {someProperty: "bookmark", parent: nil}
    #2 {someProperty: "history", parent: nil }
    #2 {someProperty: "window", parent: nil }
    

    只有当父对象包含具有SomeProperty的子对象时,父对象才有效 history window .

    我将控制器内的父级设置为:

    p = Parent.new(params[:data])
    for type in %w[bookmark_id history_id window_id]
        if !params[type].blank?
            p.children << Child.find(params[type])
        end
    end
    // save the parent object p now
    p.save!
    

    将子项分配给父项时, << ,它们不会立即保存,因为父级的ID不存在。要保存父级,它必须至少有这两个子级。我怎么解决这个问题?欢迎任何意见。

    2 回复  |  直到 15 年前
        1
  •  5
  •   Milan Novota    15 年前

    不知道你为什么要做这样的事,但无论如何,这样做怎么样?

    class Parent < ActiveRecord::Base
    
      CHILDREN_TYPES = %w[bookmark_id history_id window_id]
      CHILDREN_TYPES.each{ |c| attr_accessor c }
    
      has_many :children
    
      before_validation :assign_children
      validate :ensure_has_proper_children
    
    private
    
      def assign_children
        CHILDREN_TYPES.each do |t|
          children << Child.find(send(t)) unless send(t).blank?
        end
      end
    
      def ensure_has_proper_children
        # Test if the potential children meet the criteria and add errors to :base if they don't
      end
    end
    

    控制器:

    ...
    p = Parent.new(params[:data])
    p.save!
    ...
    

    如您所见,我首先将所有的逻辑转移到模型中。然后,有一个两步的过程来拯救孩子。首先,我们将子项分配给父项,然后验证它们是否满足所需的条件(在其中插入逻辑)。

    对不起,我矮了。如有必要,我会回答更多的问题。

        2
  •  1
  •   nas    15 年前

    首先,如果您希望保存没有父ID的子项,那么这样做没有意义

     p = Parent.new(params[:data])
     for type in %w[bookmark_id history_id window_id]
       if !params[type].blank?
         p.children << Child.find(params[type])
       end
     end
    

    的全部目的

     p.children << some_child
    

    是将父ID附加到子对象,因为父对象尚不存在,所以在此不执行此操作。

    另一件事是,如果您只想确保父对象具有子对象,并且如果您同时创建子对象和父对象,则可以在父对象和子对象创建周围使用事务块,以确保父对象具有子对象,例如

     transaction do
       p = create_parent
       p.children << child1
       p.children << child2
     end
    

    因此,在事务中,如果在任何阶段代码失败,那么它将回滚整个DB事务,也就是说,如果您要查找的是结束状态,那么您要么有一个父级,有两个子级,要么什么都没有。

    编辑:因为只有两个子对象才能创建父对象,所以在这种情况下,不能创建父对象。

    p = Parent.new(params[:data])
     for type in %w[bookmark_id history_id window_id]
       if !params[type].blank?
         p.children << Child.find(params[type])
       end
     end
    

     children = []
     for type in %w[bookmark_id history_id window_id]
       if !params[type].blank?
         children << Child.find(params[type])
       end
     end
    
     if children.size >= 2
       p = Parent.create!(params[:data])
       children.each {|child| p.children << child}
     end
    

    这有道理吗