第3回 ActiveRecordを使ったソースコードを読もう

倉貫 義人
松村 章弘
TIS株式会社
SonicGarden

2009/1/28

データの検索

 では、実際にそのUserモデルを使っているソースコードを読んでいきましょう。まずは、データベースからデータを検索して取得する部分からです。

 Userモデルを利用して、Usersテーブルからデータを検索する際に使うのは、ActiveRecordに用意されたfindメソッドを使います。findメソッドは、クラスメソッドとして用意されています。

 では実際に、Userモデルのfindメソッドを呼び出すソースコードはどこに記述するのが一般的でしょうか。それが、MVCのCに当たるコントローラ部分になります。コントローラ部分は、ユーザーからのアクションを受けて処理が実行されますが、その中でMVCのMであるモデルを呼び出します。

 参考となるソースコードですが、管理機能のユーザー情報の編集をする画面を表示するアクション部分で説明します。ユーザー情報の一覧画面から「編集」というリンクをクリックすると、「ユーザ情報編集画面」へ遷移するのですが、その際の処理が、指定された任意のユーザー情報を検索して画面に表示するという動きになっています。

図2 ユーザ情報編集画面への遷移

 この「編集」をクリックして実行される部分のソースコードは以下です。

45
46
47
48
49
50
51
52
53
54
55
def edit
  @user = Admin::User.find(params[:id])
  @user_profile = @user.user_profile
  @user_uid = @user.master_user_uid

  @topics = [[_('Listing %{model}') % {:model => _('user')}, admin_users_path],
              _('Editing %{model}') % {:model => @user.topic_title }]
rescue ActiveRecord::RecordNotFound => e
  flash[:notice] = _('ご指定のユーザは存在しません。')
  redirect_to admin_users_path
end
app/admin/controllers/users_controller.rb

 46行目で、Usersテーブルからデータを検索するためにfindメソッドを呼び出しています。Webブラウザからのリクエストに含まれるパラメータのうち、idを引数にして呼び出していることが分かります。これによって、実際にはSQLでは以下のようなクエリが発行されることになります(例:params[id]を37とします)。

SELECT * FROM users where id = 37;

 取得したデータは、上記ソースコードでいえば、@userという変数に代入されて、Rubyオブジェクトとして扱うことができるようになります。Usersテーブルのカラム名が、そのままオブジェクトのアクセッサとなり、値の取得が可能となっているのです。

 このように、ActiveRecordを使うことで、SQLを直接書かなくても、Rubyのコードだけでデータベースにアクセスするコードを記述することができるのです。

データの更新

 データの取得の次は、データの更新処理について読んでいきましょう。対応するテーブルのカラム名が、オブジェクトのアクセッサになるということは前述のとおりです。

 Usersテーブルにはnameというカラムが存在しています。前述のfindメソッドを使って取得したオブジェクトを、@userという変数に代入したとすると、@user.nameと記述することで検索したレコードのnameカラムの値を取得できます。

 逆に、@user.nameに対して値を代入することも可能です。ただし、代入の処理を記述しただけでは、まだデータベースへの保存はされておらず、メモリ上で変更されているにすぎません。

 そこで、saveメソッドを使います。値を変更したオブジェクトのsaveメソッドを呼ぶことで、実際のデータベースに変更が反映、保存されます。新規のレコードを作成する際は、findメソッドを使わずにnewメソッドを使って作成できます。

 実際のソースコードは、前節のデータの検索で表示した「ユーザ情報編集画面」から「更新」ボタンを押した際のアクションで読んでみることにします。

図3 ユーザ情報編集画面

 以下のソースコードは、既存のデータを上書きする操作になっています。

57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def update
  @user = Admin::User.make_user_by_id(params)
  if @user.id == current_user.id and (@user.status != current_user.status or @user.admin != current_user.admin)
    @user.status = current_user.status
    @user.admin = current_user.admin
    @user.errors.add_to_base(_('You cannot update status and admin of yourself'))
    raise ActiveRecord::RecordInvalid.new(@user)
  end
  @user.save!
  flash[:notice] = _('更新しました。')
  redirect_to :action => "edit"
rescue ActiveRecord::RecordNotFound => e
  flash[:notice] = _('ご指定のユーザは存在しません。')
  redirect_to admin_users_path
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotSaved => e
  @topics = [[_('Listing %{model}') % {:model => _('user')}, admin_users_path],
              _('Editing %{model}') % {:model => @user.topic_title }]
  render :action => 'edit'
end
app/admin/controllers/users_controller.rb

 58行目で用意した@userオブジェクトに対して、65行目でsave!メソッドを使って保存を行っています。!マークの付いたsave!メソッドでは、データの保存に失敗した場合に例外をスローするようになります。従って、失敗した場合のことは気にせずに、66行目、67行目の正常系の処理が続いています。

 save!の直前の59行目から64行目の処理は、この管理機能の仕様の「編集者自身が本人のデータを編集できない」という部分を実装しています。60行目のstatusや、61行目のadminメソッドが、カラムの情報から生成された動的なメソッドです。

 save!で、メモリにあった情報をデータベースへ保存したわけですが、その事前のデータの設定はどこで行われたのでしょうか。58行目のmake_user_by_idがその処理に当たります。このメソッドは、SKIP独自のもので、ActiveRecordを継承したモデルクラスで実装しています。以下がそのソースコードです。

82
83
84
85
86
87
88
89
90
91
def self.make_user_by_id(params, admin = false)
  check_params_keys(params, [:user])
  user = Admin::User.find(params[:id])
  user.attributes = params[:user]
  if !params[:user][:status].blank? and !user.unused?
    user.status = params[:user][:status]
  end
  user.admin = params[:user][:admin]
  user
end
app/models/user.rb

 84行目が前節のデータ検索で行ったfindメソッドを使っている個所です。次の85行目でパラメータを代入しています。このメソッドの最後の90行目でuserオブジェクトを返していますが、この時点ではデータベースに保存はされておらず、上記のコントローラの処理で保存されることになります。

2/4

Index
ActiveRecordを使ったソースコードを読もう
  Page1
SKIPバージョン1.0リリース!
MVCモデルのM
Page2
データの検索
データの更新
  Page3
値の検証(バリデーション)
コールバック
  Page4
テーブルの関連
トランザクション

Railsコードリーディング 〜scaffoldのその先へ〜

 Ruby/Rails関連記事
プログラミングは人生だ
まつもと ゆきひろのコーディング天国
 ときにプログラミングはスポーツであり、ときにプログラミングは創造である。楽しいプログラミングは人生をより実りあるものにしてくれる
生産性を向上させるRuby向け統合開発環境カタログ
Ruby on Rails 2.0も強力サポート
 生産性が高いと評判のプログラミング言語「Ruby」。統合開発環境を整えることで、さらに効率的なプログラミングが可能になる
かんたんAjax開発をするためのRailsの基礎知識
Ruby on RailsのRJSでかんたんAjax開発(前編)
 実はAjaxアプリケーション開発はあなたが思うよりも簡単です。まずはRuby on Railsの基礎知識から学びましょう
Praggerとnetpbmで作る画像→AA変換ツール
Rubyを使って何か面白いものを作ってみよう!
 一般的な画像をアスキーアートに変換するツールを作ってみる。さらに出力にバリエーションを持たせてみよう
コードリーディングを始めよう
Railsコードリーディング〜scaffoldのその先へ〜(1)
 優れたプログラマはコードを書くのと同じくらい、読みこなす。優れたコードを読むことで自身のスキルも上達するのだ
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間