Active Recordの使い方

2010年10月13日(水)
朝倉 慎一

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句に変換
株式会社日立ソリューションズ

技術開発本部 Rubyセンタ所属
1973生まれ。入社後、グループウェア、Webアプリケーションサーバなどの技術サポート業務に従事。現在は、RubyおよびRuby on Railsを中心にしたビジネスを推進中。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています