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!!」