AWDwR "Chapter14 Active Record Basics" つづき

あー、間が開いちゃった…。さて、前回の続きで"Relationships between Tables"から。「こっちのテーブルがあっちのテーブルに関連付けられていて、そっちのテーブルがこっちのテーブルを参照して…」とややこしくてしかも英語だから読むのにチカレマシタヨ。


以下の例で扱うテーブルの関係はこうね。

Orders (1) ← (*) LineItems (1) → (1) Products

One-to-One

まずはOne-to-Oneはこんな感じ。

class Products < ActiveRecord::Base
  has_one :line_item
end

class LineItem < ActiveRecord::Base
  belongs_to :product
end

belongs_toは外部キーがあるテーブルのモデルの方に書くよ。

has_oneで関係付けられたオブジェクトは自動的に保存されるけど、belongs_toで関係付けられたオブジェクトは自動的に保存されない。また子供のオブジェクトの保存に失敗してもActiveRecordは何も言わないので実際は以下のようにやりましょう。

item = LineItem.new
item.save!
product.item = item

belongs_to()で渡せるオプションは:class_name, :foreign_key, :conditions。
belongs_to()でクラスに自動的に追加されるメソッドは以下の通り。

item.product
item.product = Product.new
item.build_product  # DBに保存しない
item.create_product # DBに保存する

has_one()で渡せるオプションは:class_name, :foreign_key, :conditionsの他に:dependent, :orderっちゅーのがあって、:dependent => trueとすると親を削除すると子も自動的に削除されるようになりまっせ。

One-to-Many

One-to-Manyはこんな感じ。

class Orders < ActiveRecord::Base
  has_many :line_item
end

class LineItem < ActiveRecord::Base
  belongs_to :order
end

こーするとorder.line_itemsを配列のように扱える。またLineItemをOrderに追加するにはappend operatorとゆーのがあって

order.line_items << LineItem.new()

とゆーふーにする。

has_many()で渡せるオプションは:class_name, :foreign_key, :conditions, :order, :dependent以外に:exclusively_dependent, :finder_sql, :counter_sqlがあって、:exclusively_dependentは子テーブルの親が唯一でdelete時のhookメソッドがない場合に指定できる。指定すると子テーブルのレコードを1SQLで削除するようになるらしい。親が唯一でない時に指定しちゃったらどうなるんでしょ? :finder_sql, :counter_sqlで子のレコードをかきあつめるSQLを指定することができる。

has_many()でクラスに自動的に追加されるメソッドは以下の通り。

line_items()
line_items << line_item
line_items.push(line_item)
line_items.delete(line_item)
line_items.clear
line_items.find(...)
line_items.build(...)
line_items.create(...)

Many-to-Many

categoriesテーブルとproductsテーブルをmany-to-manyで関連付ける場合はcategories_productsというjoin tableを作成しておきましょう。join tableの名前の順番はアルファベット順にしなきゃダメよ。

で、モデルはこんな感じ。

class Category < ActiveRecord::Base
  has_and_belongs_to_many :products
end

class Product < ActiveRecord::Base
  has_and_belongs_to_many :categories
end

その他

自分で自分を参照する場合は

class Employee < ActiveRecord::Base
  belongs_to :manager,
             :class_name => "Employee",
             :foreign_key => "manager_id"
  has_many :managed_employee,
           :class_name => "Employee",
           :foreign_key => "manager_id"
end

とする。

子の数を頻繁に数えたい場合はcounter cachingという機能があって、belongs_to()のオプションに:counter_cache => trueを指定し、テーブルに「[子テーブルの名前]_counter」というカラムを定義しておきましょう。

トランザクション

さーて、トランザクション行っちゃうよ。簡単、簡単。

Account.transaction do
  account1.deposit(100)
  account2.withdrawa(100)
end

ブロック内の処理が正常に終了したらcommit! 例外が上がったらrollback! でもこのままだとrollbackの時DBの値は変更されないけどモデルの値は変更されてしまう。モデルの値も変更したくない場合は

Account.transaction(account1, account2) do
  account1.deposit(100)
  account2.withdrawa(100)
end

としましょう。


ふぅ、終わった。次は「もっとActive Record!!」