Active Recordの使い方
5. モデル間の関連とクエリー・インタフェース
Active Recordでは、モデル間の関連を定義できます。定義できるのは、1対多、1対1、多対多、多態的関連、の4種です。多態的関連とは、1つのテーブルを複数のモデルで共有する形式のことであり、STI(Single Table Instance)と呼ばれます。モデル間の関連を定義することで、関連するデータの取得や追加が、簡単にできるようになります。
モデル間に関連を持たせるには、参照元となるテーブルに、外部キーとなる列が必要です。このため、テーブル作成時に定義しておく必要があります。
以下に、例を示します。図2は、「フォーラム内に複数のトピックがある」という、1対多の関連を示しています。この場合、has_many、belongs_toを使って、関連を定義します。dependentオプションは、親レコードが削除されたときの子の削除規則を設定するものです。
図2: 1対多の関連(クリックで拡大) |
関連を定義すると、関連名を使用することにより、以下のように、関連するテーブル間の操作ができるようになります。
forum = Forum.create(:title => "Rubyフォーラム") #フォーラム作成 topic = Topic.create(:subject => "Rubyトピック", :forum => forum) #上記フォーラムに属するトピックとして作成
forum = Forum.first #フォーラムを取得 topic = Topic.create(:subject => "Railsトピック") #トピックを作成 forum.topics << topic #作成したトピックをフォーラムに追加
forum = Forum.first #フォーラムを取得 topics = forum.topics #フォーラムに属するトピックを取得
Active Recordを使ってクラスを定義すると、テーブル・データを操作する各種メソッドが用意されます。Rails 3.0になって大きく変わった点は、データベースからデータを取得するメソッドです。以前は、データ取得を行うfindメソッドに対して、Hashを使ってさまざまな条件を設定していました。Rails 3.0からは、メソッド・チェインでクエリーを組み立てる方式になりました。主なメソッドを、表2に示します。
表2: データ取得用の主なメソッド
メソッド名 | 概要 |
---|---|
find | 主キーを指定してレコードを取得。先頭レコードを取得するfirst、最終レコードを取得するlast、すべてのレコードを取得するallもあります。 |
where | WHERE句を設定します。 |
select | 特定の列だけ取得します。selectで列を指定していない場合は、*が使用されます。 |
group | GROUP BY句を設定します。 |
order | ORDER BY句を設定します。 |
limit | LIMIT句を設定します。 |
offset | OFFSET句を設定します。 |
joins | JOIN句を設定します。 |
includes | 関連するテーブルから必要なレコードをまとめて読むeager loadを行います。 |
lock | 悲観的ロックを行います。 |
readonly | 読み込み専用でレコードを取得します。 |
from | データベースビューなどマップされたテーブル以外から読み込む際に使用します。 |
表2のメソッドを使用した例を、以下に示します。findメソッド使用してデータを取得する、簡単な例です。
forum = Forum.find(1) #idが1のレコードを取得 forum = Forum.first #最初のレコードを取得 forums = Forum.all #すべてのレコードを取得
whereメソッドなどを使用して、抽出条件を設定します。find(all、first、lastを含む)以外のメソッドは、データベースにはアクセスしません。これらは、SQL文を組み立てるために必要な情報が入ったActiveRecord::Relationオブジェクトを返し、実際にデータが必要になるまでデータベースにはアクセスしない遅延ロードとなっています。以下の例では、最後の行でデータベースにアクセスして、データの取得を行います。
forum = Forum.where(:locked => false).order("created_at") #抽出条件の指定 forum = forum.joins(:topics) #テーブルの連結 topics = Topic.where("status = ?", ["published"]) #パラメータ化クエリーの例 forum_topics = forum & topics #上記条件のマージ forum_topics.all #データの取得
また、Rubyの範囲演算子を使うとBETWEEN演算子になり、抽出条件が配列の場合はIN句を使うようになります。
forum = Forum.where(:created_at => Date.yesterday.to_time .. Time.now) #BETWEEN演算子 topics = Topic.where(:status => ["published","draft"]) #配列の場合は、IN句に変換