2017-01-26 5 views
1

同じ(新しく作成された)子を持つ可能性のある新しいレコードの作成には問題があります。 もっと簡単にするために、rulesという名前のテーブル(ファイアウォールのアクセス制御リストのルールのような)とipsという表があり、ip_strという1つのUNIQUE INDEXというed属性を持つテーブルがあるとしましょう。同等の属性を持つRailsオブジェクトのマージ

Ruleを作成するには、ソースIpと宛先Ipが必要です。私はRuleを保存しようとするとき

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

それは私たちのケースであり、ここで

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ようdeny ip any anyを、使用することは珍しいことではない、ということがありますデータベースにip_str = "any"Ipがない場合、Railsは2つの別々のオブジェクトをメモリに保持し、相互に後でそれらを保存しようとします。最初にIpを保存すると、UNIQUE INDEXのために、まだ存在しないと考えられていた2番目のIpを保存しようとしましたが、エラーで終了します。 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_initialize'の代わりに' first_or_create'を使用することをお勧めしますか?それを使って最初の 'Ip'を保存し、次に別のもの(等しいattrsで)を保存しようとすると、前に保存された最初のものが得られます。 2つの変数 - 1つのオブジェクト。 – VAD

+0

ありがとうございます。モデルオブジェクトを初期化すると即座にモデルオブジェクトを即座に保存することができます。その場合、オブジェクトとその子を 'トランザクション 'で作成するコードブロック全体をラップしなければならないので、スムーズに'ロールバックすることができます。最初のエラーが発生するとすぐにトランザクションを中止するので、スムーズにすべてのactiverecordエラーを生成するかどうかはわかりません。 – Brauser

+0

このソリューションはあなたのユースケースで役立つことができてうれしいです。答えとして追加しました – VAD

答えて

1

許容できる場合はfirst_or_initializeの代わりにfirst_or_createを使用することができます。

最初にIpを保存し、次に別のもの(attrsと等しい)を保存しようとすると、前に保存された最初のものが表示されます。 2つの変数 - 1つのオブジェクト。

関連する問題