2010-02-03 67 views
4

我在has_many关系上存在验证问题,但是父项不存在。但是,在创建/保存父对象时,我想确保特定的子项(具有某些属性)已经保存。对has_many关联的Rails验证

有一个Parent对象,has_manyChild对象。 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 historywindow对象。

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不存在:

我为建立父控制器内。而且父母要得救,至少必须有两个孩子。我怎么能解决这个问题?任何输入是受欢迎的。

回答

5

不当然,为什么你需要做这样的事情,但无论如何,这样做呢?

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! 
... 

正如你所看到的,我感动了所有的逻辑放在首位模型。然后,有两步拯救孩子的过程。首先,我们将孩子分配给家长,然后验证他们是否符合所需的标准(在那里插入您的逻辑)。

对不起,我很抱歉。如有必要,我会回答任何进一步的问题。

+0

我喜欢这种方法,因为它保持模型内的逻辑。 send(t)函数在这里做什么?它是否被父对象调用? – Anurag 2010-02-04 07:18:21

+0

是的,因为我们将子类型动态地定义为父对象的访问者(属性),所以我们也需要动态访问它们。这就是为什么我们需要调用父对象的send方法。 – 2010-02-04 11:58:44

1

第一件事情,如果你想要孩子要保存而不父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 

因此,本次交易中,如果在任何阶段,代码失败,那么它会回滚整个数据库的事务,也就是你要么有父母一方有2个孩子或没有,如果这是最终状态你正在寻找。

编辑:既然你不能创建一个父除非它有2个孩子,在这种情况下,而不是

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 

这是否有道理

+0

感谢您的回答。我并不特意要保存没有父母身份的孩子,而只是在这个过程中始终创建孩子。那时我不会有任何有关父母的信息。父母的信息将出现在稍后的HTTP请求中。孩子的物品是一次性的,我不在乎他们是否没有父母,因为cron工作会定期清理它们。我可以在技术上使用会话,但这肯定会杀死服务器,因为子对象真的很重,所以我选择直接在数据库中存储em。 – Anurag 2010-02-03 07:05:20

+0

正确我明白你的意思,我会编辑我的答案,然后 – nas 2010-02-03 07:47:33