同じ(新しく作成された)子を持つ可能性のある新しいレコードの作成には問題があります。 もっと簡単にするために、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
は絶対に重要です。
'first_or_initialize'の代わりに' first_or_create'を使用することをお勧めしますか?それを使って最初の 'Ip'を保存し、次に別のもの(等しいattrsで)を保存しようとすると、前に保存された最初のものが得られます。 2つの変数 - 1つのオブジェクト。 – VAD
ありがとうございます。モデルオブジェクトを初期化すると即座にモデルオブジェクトを即座に保存することができます。その場合、オブジェクトとその子を 'トランザクション 'で作成するコードブロック全体をラップしなければならないので、スムーズに'ロールバックすることができます。最初のエラーが発生するとすぐにトランザクションを中止するので、スムーズにすべてのactiverecordエラーを生成するかどうかはわかりません。 – Brauser
このソリューションはあなたのユースケースで役立つことができてうれしいです。答えとして追加しました – VAD