简体   繁体   English

针对has_many关联的Rails验证

[英]Rails validation for a has_many association

I am having trouble with validations on a has_many relationship where the children exist, but the parent doesn't. 我在孩子存在的has_many关系上遇到验证时遇到问题,但父母却没有。 However, when creating/saving the parent object, I want to ensure that specific children (with certain attributes) have already been saved. 但是,在创建/保存父对象时,我想确保已保存特定的子级(具有某些属性)。

There is a Parent object that has_many Child objects. 有一个Parent对象has_many Child对象。 The Child objects are persisted into the database first, and thus don't have any reference to the parent. Child对象首先保留在数据库中,因此没有对父对象的任何引用。 The association structure is: 关联结构是:

Parent
  - has_many :children 

Child
  - someProperty: string
  - belongs_to: parent

For example, there are three child objects: 例如,有三个子对象:

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

A parent is valid only if it contains child objects with someProperty history and window . 仅当父项包含具有someProperty historywindow子对象时,父项才有效。

I am setting up the parent inside the controller as: 我在控制器中设置父节点:

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!

When the children are assigned to the parent with << , they are not saved immediately as the parent's id does not exist. 当子项被分配给具有<<的父项时,它们不会立即保存,因为父项的id不存在。 And for the parent to be saved, it must have at least those 2 children. 为了保存父母,它必须至少有这2个孩子。 How could I solve this problem? 我怎么能解决这个问题? Any input is welcome. 欢迎任何输入。

Not sure why you need to do such a thing, but anyway, how about doing this? 不知道为什么你需要做这样的事情,但无论如何,这样做怎么样?

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

Controller: 控制器:

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

As you can see, I moved all the logic to model at the first place. 如您所见,我首先将所有逻辑移至模型。 Then, there is a two-step process for saving children. 然后,有一个两步拯救儿童的过程。 First, we assign children to the parent and then we validate if they meet the required criteria (insert your logic there). 首先,我们将子项分配给父项,然后验证它们是否符合所需条件(在那里插入逻辑)。

Sorry for being short. 抱歉做空。 I'll answer any further questions if necessary. 如有必要,我会回答任何进一步的问题。

First thing, if you want the children to be saved without the parent id then there is no point in doing this 首先,如果您希望在没有父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

the whole purpose of 的全部目的

 p.children << some_child

is to attach the parent id to the child object which you are not doing here because the parent doesn't exist yet. 是将父ID附加到您未在此处执行的子对象,因为父级尚不存在。

The other thing is if you just want to make sure that the parent has a child object and if you are creating child and parent together then you can use transaction block around the parent and child creation which will make sure that the parent has child, like 另一件事是,如果你只是想确保父对象有一个子对象,如果你在一起创建子对象和父对象,那么你可以在父和子创建周围使用事务块,这将确保父有子,如

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

So, within the transaction, if at any stage code fails then it will rollback the whole db transaction , ie you will either have one parent with 2 children or nothing, if that's the end state you are looking for. 因此,在事务中,如果在任何阶段代码失败,那么它将回滚整个数据库事务,即,如果这是您正在寻找的最终状态,您将要么有一个父项有2个子项,或者什么也没有。

EDIT: Since you can't create a parent unless it has 2 children, in that case, instead of 编辑:因为你不能创建父,除非它有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

do

 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

Does that make sense 那有意义吗

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM