合并具有相等属性的Rails对象



我在创建可能具有相同(也是新创建的)子项的新记录时遇到问题。为了简化操作,我们假设我有一个名为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 anyRule并不罕见,在我们的情况下,它相当于

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,然后你尝试保存另一个(具有相等属性),你只得到了之前保存的第一个。两个变量-一个对象。

最新更新