2017-01-26 28 views
1

我有创造新记录潜在具有相同(也新建)儿童问题的对象。为了方便起见,我们假设我有一个名为rules的表(如防火墙的访问控制列表中的规则)和名为ips的表包含名为ip_str的单个UNIQUE INDEX ed属性。合并Rails的平等属性

要创建Rule,需要源Ip和目标Ip

class Rule < ActiveRecord::Base 
    belongs_to :src_ip, class_name: Ip.name 
    belongs_to :dest_ip, class_name: Ip.name 
end 

的情况并不少见使用Ruledeny 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和有在数据库中没有Ipip_str = "any",Rails的在内存中保持两个独立Ip -objects并尝试一个接一个将其保存。因此,保存第一个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 

可悲的是真实的世界是复杂得多,我可能在differenct孩子的同时节省了大量的记录楷模。是的,UNIQUE INDEX是至关重要的。

+2

也许这是可以接受的,你使用'first_or_create'取代'first_or_initialize'?随着您保存第一个'Ip',下次你再节省一个(具有相等的attrs),你只得到以前保存的第一个。两个变量 - 一个对象。 – VAD

+0

谢谢,你是对的,瞬间就被初始化将解决保存模型对象。在这种情况下,我必须包装整个代码块,它负责在一个'transaction'中创建一个对象及其子元素,因此可以顺利地'回滚';但不知道这样做是否也能平稳地生成所有activerecord错误,因为只要发生第一个错误就会中止事务。 – Brauser

+0

很高兴这个解决方案在你的用例中很有用。添加它作为答案 – VAD

回答

1

如果可以接受,可以在这里使用first_or_create而不是first_or_initialize

因此,您保存第一个Ip,接下来您尝试保存另一个(与attrs相等),然后您只保存以前保存的第一个。两个变量 - 一个对象。