エンタープライズ領域での採用も増えてきたRuby on Railsを使ってWebアプリケーションを作るための入門連載。最新版の4に対応しています。今回は、ActiveRecordにおけるモデルオブジェクト同士のつながりを表す「関連」とイベントが発生した際に実行したい処理を指定しておく「コールバック」などについて。
前回の「ActiveRecordの基本機能とマイグレーション、バリデーション」からRailsのモデルの機能を担うActiveRecordについて学んできました。今回はモデル間の「関連」「コールバック」について解説していきます。
これまでの連載と同様、サンプルプロジェクトの「book_library」を拡張して試してみましょう。
ここでいう「関連」(associations)とは、「モデルオブジェクト同士のつながり」のことを指しています。「関連」し合うモデルの数により「一対一」「一対多」「多対多」の「関連」があり、ActiveRecordには、これらを簡単に扱える仕組みが用意されています。
まずはシンプルな「一対多の関連」から見ていきましょう。
「一対多の関連」は、ある一つのモデルオブジェクトが多数の別のモデルオブジェクトから参照される「関連」のことです。
試しに簡単なものを実装してみましょう。次のコマンドで「関連」のための外部キー(参照先テーブルの主キー)を格納するカラムを作るマイグレーションを実行しましょう。
% rails g migration AddUserIdToBook user_id:integer % rake db:migrate
これによりbooksテーブルに対して外部キー(usersの主キー)を格納する「user_id」というカラムが追加されました。この変更はデータベースに対してのもので、アプリケーション側では、まだBookモデルに「user_id」という属性が追加されただけです。そこで、Bookモデルに「関連」の定義を追加してuser_id属性でUserモデルを参照するよう変更します。
class Book < ActiveRecord::Base belongs_to :user end
この「belongs_to」を追加することでBookモデルはUserモデルと「関連」し、次のように設定したり参照したりすることができるようになります。
book = Book.create(title: "現場で使えるRuby on Rails") book.user = User.create(name: "アリス", department: "開発部") book.user #=> #<User id: 1, name: "アリス", department: "開発部", ...> book.user_id #=> 1
また、Userモデル側から自身の主キーを持つBookモデルを参照するには、次のように「has_many」で「関連」を定義します。
class User < ActiveRecord::Base has_many :books end
これによりUserモデルオブジェクトは「関連」するBookモデルオブジェクトを次のように設定したり参照したりすることができるようになります。
user = User.create(name: "ボブ", department: "会計部") user.books.create(title: "現場で使えるRuby on Rails第二版") user.books << Book.create(title: "現場で使えるRuby on Rails第三版") user.books #=> #<ActiveRecord::Associations::CollectionProxy [ #<Book id: 2, title: "現場で使えるRuby on Rails第二版", user_id: 3, ...>, #<Book id: 3, title: "現場で使えるRuby on Rails第三版", user_id: 3, ...> ]> user.books.build(title: "現場で使えるRuby on Rails第四版") #=> #<Book id: nil, title: "現場で使えるRuby on Rails第四版", user_id: 3, ...> # 未保存のBookモデルオブジェクト # 保存するには user.books.last.save などを呼び出します。
このような「belongs_to」と「has_many」を使った「関連」を「一対多の関連」といいます。この親子関係の「関連」は非常によく使うため、ここで応用的な使い方についても見ていきましょう。
「belongs_to」はオプションを使うことで、より便利に「関連」を扱えるようになります。ここで代表的なものについて紹介します。
シンボル「:destroy」の値を持たせると、そのモデルオブジェクトが「destroy」メソッドにより削除されたときに、参照しているモデルオブジェクトの「destroy」メソッドも実行して削除します。
標準では、「関連」名とする第1引数のシンボルに「_id」を付けた名前のカラムが外部キーを格納するものとして設定されています。しかし、それ以外の名前のカラムを使いたい場合には、このオプションの値に指定するカラム名の文字列を渡すことで、そのように設定できます。
「関連」名と参照先モデル名が異なる場合、その「関連」が対象とするモデルを指定するために使います。具体的には次のように使います。
class Book < ActiveRecord::Base belongs_to :holder, class_name: 'User', foreign_key: 'user_id' end
「has_many」もオプションを使うことで標準の設定を変更したり機能を追加したりすることができます。
「belongs_to」の「:dependent」オプションと同じように「関連」するモデルオブジェクトを削除します。「:destroy」を値として渡すと、全ての「関連」するオブジェクトも「destroy」メソッドを呼び削除します。「:delete_all」を値に渡すと、後述する「コールバック」なしで「関連」するオブジェクトのレコードにSQLのDELETE文を実行します。
参照元モデルにおいて、「関連」名に「_id」を付けた名前ではないカラムを外部キーの格納場所としている場合、そのことを知るために参照先モデル側でも「:foregin_key」オプションを持っておく必要があります。
「関連」名が小文字の参照元モデル名の複数形と異なる場合、その「関連」が対象とするモデルを指定するために使います。
「一対一の関連」は「一対多」の場合とほとんど同じ構成をとります。データベースでは「関連」名に「_id」を付けた名前のカラムを作ります。アプリケーション側では参照元モデルは「belongs_to」で定義し、参照先モデルでは「has_many」の代わりに「has_one」を使って「関連」を定義します。
「has_one」は引数にとる「関連」名が単数形になっている点に注意してください。
class User < ActiveRecord::Base has_one :book end
この例では、Userモデルは単一のBookモデルとの間に「関連」を持ちます。これにより、次のようにメソッドが使えるようになります。
user = User.create(name: 'チャーリー', department: '秘書課') user.book = Book.create(title: '入門ブラジル語') user.book #=> #<Book id: 4, title: "入門ブラジル語", user_id: 4, ...>
「has_one」の「:class_name」オプションや「:foreign_key」オプションは「has_many」の場合と同じです。
一方で「:dependent」の取り得る値は複数を対象とする「:delete_all」がなくなり単一のレコードを指す意味で「:delete」になっています。
Copyright © ITmedia, Inc. All Rights Reserved.