我在创建可能具有相同(也是新创建的)子项的新记录时遇到问题。为了简化操作,我们假设我有一个名为rules
的表(如防火墙访问控制列表中的规则)和一个包含单个UNIQUE INDEX
-ed属性ip_str
的表ips
。
为了创建Rule
,需要源Ip
和目的地Ip
。
class Rule < ActiveRecord::Base
belongs_to :src_ip, class_name: Ip.name
belongs_to :dest_ip, class_name: Ip.name
end
使用类似deny ip any any
的Rule
并不罕见,在我们的情况下,它相当于
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_str = "any"
的Ip
时,Rails会在内存中保留两个单独的Ip
-对象,并尝试一个接一个地保存它们。因此,保存第一个Ip
成功,然后它试图保存第二个Ip
,它认为它还不存在,但由于UNIQUE INDEX
的原因,最终出现了错误。有没有一种方法可以告诉Rails在尝试保存之前合并内存中的"重复"对象?
上面给出的例子的一个明显的解决方案是下面的
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
遗憾的是,现实世界要复杂得多,我可能会在不同的儿童模型中同时保存大量记录。是的,UNIQUE INDEX
绝对至关重要。
如果可以接受,可以在此处使用first_or_create
而不是first_or_initialize
。
所以你保存了第一个Ip
,然后你尝试保存另一个(具有相等属性),你只得到了之前保存的第一个。两个变量-一个对象。