プログラマとプロマネのあいだ

プログラマもやるし、プロマネもやるし、たまに似非アーキとか営業っぽいこともやる

「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

とした方がいいでしょうね。