検索
連載

「ActiveRecord」の基本とデータの参照Ruby on Rails3で学ぶWeb開発のキホン(3)(2/2 ページ)

今回はRailsの最も重要なライブラリの1つ、ActiveRecordについて、基本的な使い方をご紹介します。

Share
Tweet
LINE
Hatena
前のページへ |       

マイグレーションとは

 マイグレーションは、データベースのスキーマの変更をプロジェクトのレベルで管理するもので、スキーマを複数の開発者の間で共有することを助けてくれます。具体的には、データベースのスキーマに変更がある度に、その変更をデータベースに反映するためのRubyコードを記述します。また、その変更を取り消すためのコードも同じマイグレーションファイル内に記述します。どの変更が実際にDBに反映されているかの状態はschema_migrationsテーブルに保持されます。これによって、ほかの開発者が新しいスキーマ変更処理を取り込んだり、任意のバージョンまで状態を戻したりすることができるようになります。

 複数の開発者の間で、どのようにマイグレーションが利用されるかのイメージを図にすると、次のようになります。まず、スキーマへの変更が発生したら、対応するマイグレーションファイルを作成して、自分の開発環境に反映するとともに、ソースコードを管理するためのレポジトリに追加します。別の開発者は、新しく追加されたマイグレーションファイルをレポジトリから取得し、自分の開発環境でマイグレーションを実行して、変更をデータベースに反映することができます。

マイグレーションファイルによる開発者間での協調作業の概念図
マイグレーションファイルによる開発者間での協調作業の概念図

 マイグレーションを開発環境に反映させるには、rakeコマンドを用います。なお、オプションなどについては、本連載の第1回の解説を参照してください。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 それでは、マイグレーションファイルの内容を見ていきましょう。

 先ほどのUserクラスの例では、rails gコマンドで次のようなマイグレーションファイルが生成されます。このマイグレーションファイルは、「usersというテーブルを作成する」というスキーマ変更に対応するとともに、「20110403005910」というスキーマバージョンを表しています。なお、このバージョンの数字は、マイグレーションファイルをコマンドで生成する際に日時をもとに自動で付与されます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

[db/migrate/20110403005910_create_users.rb]

 マイグレーションファイルの内容は、upとdownの2つのメソッドで構成されます。upにはデータベースにこの変更を反映する(このスキーマバージョンに上げる)処理を、downにはこの変更を取り消す(前のバージョンに戻す)処理を記述します。この例では、upではcreate_tableというメソッドを使ってテーブルを作成し、downではそのテーブルを削除します。

 create_tableに渡すブロックの中では、引数tに対して、t.string :name のようにカラムを記述していきます。このように、マイグレーションでは、「VARCHAR(20)」のようなデータベースのデータ型を直接具体的に指定するのではなく、抽象化されたデータ型を用います。基本的には以下のデータ型が使用できます。

  • :string
  • :text
  • :integer
  • :float
  • :decimal
  • :datetime
  • :timestamp
  • :time
  • :date
  • :binary
  • :boolean

 このような抽象化されたデータ型を使うことには、シンプルで覚えやすく、データベースに詳しくなくても簡単に開発できるというメリットがあります。さらに、データベースの種類ごとの差異を吸収してくれるため、利用するRDBMSを変えてもコードにほとんど手を加えずに済むという効果もあります。

 ちなみに、上記のマイグレーションを、デフォルトのsqlite3で実行すると、次のようなテーブルが作成されます。stringがvarchar(255)にマッピングされているのが分かりますね。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 文字列の長さは、:limitオプションを指定することで変えることができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 また、:decimal型のカラムには :precision と :scale で精度とスケールを設定できます。

 このほか、デフォルト値を設定したり、NULL不可かどうかを指定するには次のようにします。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 このほか、create_table の記述について特筆すべき点としては、プライマリキーとタイムスタンプが挙げられます。続いてこれらについて説明していきます。

プライマリキーの名前を変える

 ActiveRecordでは、テーブルは基本的にidという名前の自動発番のプライマリキーを持つという想定になっており、create_tableの記述で特に何も書かなければそのようにスキーマが作成されます。もしも規約に外れたテーブルを作りたければ、:idオプションでプライマリキーの有無を、:primary_keyオプションでプライマリキーの名前を変えることができます。例えば次の例ではプライマリキーの名前をkaiin_idにしています。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 なお、テーブルのプライマリキーの名前が何であるかに関わらず、ActiveRecordオブジェクトではプライマリキーに対応する属性名としてidが使われます。

デフォルトで追加されるタイムスタンプ

 Userクラスの例では、マイグレーションのcreate_tableブロックの最後に次のような記述があります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 これは、テーブルにタイムスタンプ用のカラムを追加するという意味で、具体的にはcreated_atとupdated_atが追加されます。これらのカラムはActiveRecordでは特別な意味を持っています。新しいActiveRecordオブジェクトがはじめてDBに保存されたときにはcreated_atとupdated_at、更新されたときにはupdated_atに、自動的にその時点の日時がセットされます。また、created_on、updated_onという日付型のカラムがあれば、日時ではなく日付を保存します。これらの動作は、こういった名前のカラムがあれば自動的に行われます(従って、timestampsメソッドではなくadd_columnなどでこれらのカラムを追加しても同じ効果が得られます)。

マイグレーションでよく使うその他のメソッド

 テーブルの作成/削除のほか、マイグレーションでは、既存のテーブルに対するカラムの操作をしたい場面もよくあります。カラム操作のためのメソッドとしては以下が用意されています。

  • add_column
  • rename_column
  • change_column
  • change_column_default
  • remove_column

 また、インデックスに関するメソッドも用意されています。

  • add_index
  • rename_index
  • remove_index

 マイグレーションでは、executeメソッドでSQLを直接実行することもできるので、用意されたメソッドでは実現できないような処理も行うことができます。

基本の検索とArel

 ここまでは、ActiveRecordの基本的な考え方や、マイグレーションについて解説しました。ここからは、個別の機能について詳しく見ていきます。更新系や複雑な処理については次回以降に譲り、今回はActiveRecordを使ったデータの検索方法について見て行くことにします。

 Rails3からActiveRecord内部でArelというライブラリが使われるようになり、以前と比べてAPIが大きく変わっています。Arelとは、データベースクエリを生成するためのライブラリで、プログラムとしてより柔軟に検索条件を組み立てることができるようになっています。

Rails2との違い

 例えばRails2では検索の際、以下のようにしていました。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 Rails3のArelを使ったやり方では以下のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 また、「order by」や「limit」を指定する場合、Rails2では以下のようしていました。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 Rails3では以下のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 次のように、各条件を順番に適用していくといったやり方もできます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 User.where(:age => 31)としたときに何が起きるのでしょうか。User.where(:age => 31).classとすると、返ってくるオブジェクトがActiveRecord::Relationというクラスのインスタンスであることが分かります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 さらに、User.where(:age => 31).limit(20)としたときに返ってくるのもActiveRecord::Relationオブジェクトです。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 つまりwhereやlimitなどのメソッド呼び出しによって連鎖的にActiveRecord::Relationオブジェクトが作られているのです。また、ActiveRecord::Relationオブジェクトを作っただけではデータベースに対して検索は行われません。例えば以下のようにしてデータを利用しようとしたタイミングで初めて検索が行われます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

検索方法

 検索方法について詳しく見て行きましょう。

 rails consoleなどを使って手元の環境で試す場合、to_sqlメソッドが役に立ちます。to_sqlメソッドを使うとActiveRecord::Relationがデータベースに対して実行するSQLを確認することができます。使い方は以下の通りです。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

プライマリキーによる検索

 プライマリキーによる検索にはfindを使います。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 データが存在しなかったときは例外(ActiveRecord::RecordNotFound)が発生します。

firstとlastでデータを1件だけ取得

 firstで最初のデータを1件取得することができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 同様に、lastで最後のデータを取得することができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 first、lastともに、データが存在しなかったときはnilが返ります。

複数データの取得

 findの引数にはidを複数指定することができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 すべてのデータを取得するにはallを使用します。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 allはすべてのデータを一度に取得します。上記のコードを実行した場合、以下のSQLが実行されます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 テーブルに大量のデータがある際にそれらを一定数ずつ取り出して処理を行いたい場合はfind_eachを使用します。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 find_eachを使った場合は以下のSQLが実行されました。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 LIMITを指定して1000件ずつデータを取得しようとしていることが分かります。:batch_sizeオプションを使って一度に取得するデータの件数を指定することもできます。以下の例では5000件ずつ取得しています。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

検索条件の指定

 検索条件を指定するにはwhereを使います。Userモデルからageが31のデータを検索する場合は以下のようにします。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 実行されるSQLは以下のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 以下のようにしてageに複数の値を指定することもできます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 このとき実行されるSQLは以下のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

プレースホルダーの使用

 whereには、SQLの一部となるような文字列を直接指定することもできます。また、その際はプレースホルダーを使って値を埋め込むことができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 なお、文字列を指定するときに、プレースホルダーを使わずに #{} などを使って直接データを埋め込むと、データにSQLインジェクションの危険性がある場合には問題となります。危険性があるデータを文字列に埋め込んで指定したい場合は、必ずプレースホルダーを使うべきです。

 プレースホルダーとして“?”の代わりにキーワードを使うこともできます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

Rangeを使った条件指定

 Rangeオブジェクトを使って範囲指定を簡単に表現することができます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 このとき実行されるSQLは以下のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 この他にもSQLの各種操作に対応した以下のようなメソッドが定義されています。

  • order
  • limit
  • offset
  • group
  • having

メソッドにカラム名を指定する「ダイナミックファインダー」

 上記の方法以外にもダイナミックファインダーと呼ばれる機能を使って検索を行うこともできます。

 例えば、以下のような検索を行いたい場合、

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 ダイナミックファインダーを使うとこのように書けます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 以下のように複数の条件を組み合わせることもできます。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 検索条件によってはダイナミックファインダーのほうがより分かりやすく書けます。状況に応じて使い分けると良いでしょう。

 ダイナミックファインダーは、あらかじめメソッドが用意されているわけではなく、呼ばれたときにはじめて使えるようになります。これは、Rubyの動的言語としての特性が大変うまく生かされている例といえます。どのように実装されているのか調べてみると面白いかもしれません。

検索条件に名前を付けられる「スコープ」

 ActiveRecordでは、whereなどを用いて検索条件を記述するだけでなく、特定の検索条件に自分で好きな名前を付けてメソッドとして使うことができます。これはスコープと呼ばれます。

 例えば、有料会員を示すspecialフラグがusersテーブルにあるとします。有料会員のみを検索する通常の書き方は次のようになります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 しかし、次のように書ければソースコードはさらに読みやすくなるでしょう。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 これは、以下のようなスコープをUserクラスに記述することで可能になります。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 スコープは、重ねて呼ぶことができます。例えば、女性の有料会員を次のように絞り込むことができるようになります。とても読みやすくなりますね!

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

まとめ

 連載第3回となる本記事では、Ruby on Railsのモデル層の標準の実装であるActiveRecordについて、基本的な考え方や操作方法、使い始めるために必要となるマイグレーションの知識、そして参照機能について、主に検索条件の組み立て方に焦点を当てて解説しました。ActiveRecordの役割や使い方に親しんでもらうとともに、データベースの検索を驚くほど柔軟に読みやすく記述できる魅力を実感していただけたなら幸いです。次回は、更新系の機能や値の検証の仕組みについて詳しく紹介していきます。

Copyright © ITmedia, Inc. All Rights Reserved.

前のページへ |       
ページトップに戻る