我在创建可能具有相同(也是新创建的)子项的新记录时遇到问题。
为了简化操作,我们假设我有一个名为rules
的表(如防火墙的访问控制列表中的规则)和一个名为ips
的表,其中包含一个名为UNIQUE INDEX
的ed属性ip_str
。
要创建Rule
,需要来源Ip
和目的地Ip
。
class Rule < ActiveRecord::Base
belongs_to :src_ip, class_name: Ip.name
belongs_to :dest_ip, class_name: Ip.name
end
使用类似Rule
的{{1}}并不罕见,在我们的情况下相当于
deny ip any any
这里显而易见的问题是,当我尝试保存src = Ip.where(ip_str: "any").first_or_initialize()
dest = Ip.where(ip_str: "any").first_or_initialize()
rule = Rule.new(src_ip: src, dest_ip: dest)
rule.save()
#=> throws error: duplicate entry
且数据库中没有Rule
Ip
时,Rails会保留两个单独的ip_str = "any"
- 内存中的对象并尝试将它们相互保存。因此,保存第一个Ip
成功,然后它会尝试保存它认为尚不存在的第二个Ip
,并因Ip
而导致错误。有没有办法告诉Rails在尝试保存之前在内存中合并“重复”对象?
上面给出的示例的一个明显的解决方案是以下
UNIQUE INDEX
可悲的是,现实世界要复杂得多,我可能会在不同的儿童模型中同时保存大量记录。是的,src = Ip.where(ip_str: "any").first_or_initialize()
dest = Ip.where(ip_str: "any").first_or_initialize()
dest = src if (src.ip_str == dest.ip_str)
rule = Rule.new(src_ip: src, dest_ip: dest)
rule.save()
#=> always succeeds
绝对至关重要。
答案 0 :(得分:1)
如果可以接受,可以在此使用first_or_create
代替first_or_initialize
。
所以你保存了第一个Ip
,然后你试着保存另一个(有相同的attrs),你只需要保存第一个。两个变量 - 一个对象。