「RailsによるアジャイルWebアプリケーション開発」18章まで読了
前回の続き。
保存のタイミング
has_oneアソシエーションに代入すると、いきなりデータベースに保存されるという、ちょっと驚きの挙動が見られます。
サンプルはこんな感じ。
require 'rubygems' require 'activerecord' ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "../db/development.sqlite3") class Order < ActiveRecord::Base has_one :invoice end class Invoice < ActiveRecord::Base belongs_to :order validates_presence_of :name end order1 = Order.new( :name => "taka_2", :address => "Kanagawa prefecture", :email => "taka_2@test.com", :payment_type_id => 1) order2 = Order.new( :name => "taka_3", :address => "Kanagawa prefecture", :email => "taka_3@test.com", :payment_type_id => 1) order3 = Order.new( :name => "taka_4", :address => "Kanagawa prefecture", :email => "taka_4@test.com", :payment_type_id => 1) inv1 = Invoice.new( :order_id => order1.id, :name => "taka_22") inv2 = Invoice.new( :order_id => order2.id, :name => "taka_33") inv3 = Invoice.new( :order_id => order3.id, :name => "") puts("Order(親)はDBにある状態でInvoice(子)を追加") order1.save! puts Order.find(order1.id) order1.invoice = inv1 # ここでInvoiceは自動で保存される puts Invoice.find(inv1.id) # 後始末 Invoice.delete(order1.invoice.id) Order.delete(order1.id) puts("") puts("Order(親)がDBに無い状態でInvoice(子)を追加") order2.invoice = inv2 # 親が保存されていないので、子はメモリに保持される begin puts Order.find(order2.id) rescue # ID指定でレコードが見つからない場合は、例外が発生する puts "Record Not Found." end begin puts Invoice.find(inv2.id) rescue # ID指定でレコードが見つからない場合は、例外が発生する puts "Record Not Found." end puts("") puts("Order(親)はDBにある状態でInvoice(子)を追加") puts("ただし、子が保存されるときに検証エラー") order3.save! order3.invoice = inv3 begin puts Order.find(order3.id) puts Invoice.find(inv3.id) rescue # ID指定でレコードが見つからない場合は、例外が発生する puts "Record Not Found." end # 後始末 Order.delete(order3.id)
結果はこう。
Order(親)はDBにある状態でInvoice(子)を追加 #<Order:0x2ff6264> #<Invoice:0x2ff213c> Order(親)がDBに無い状態でInvoice(子)を追加 Record Not Found. Record Not Found. Order(親)はDBにある状態でInvoice(子)を追加 ただし、子が保存されるときに検証エラー #<Order:0x2fec318> Record Not Found.
- 1番目は、既にデータベースに存在するOrderに対して、明示的にsaveを呼ぶことなく、Invoiceを代入しただけでデータベースに保存される例
- 2番目は、まだデータベースに存在しないOrderに対して、Invoiceを追加しても保存されませんという例
- 3番目は、既にデータベースに存在するOrderに対して、Invoiceを追加したので保存されるはずが、Invoiceが検証エラーになって実は保存されていないという例
まあ便利な機能ですが、3番目のような例もあるので、本書中に書かれている通り、
invoice = Invoice.new
invoice.save!
an_order.invoice = invoice
とした方がいいでしょうね。