Ruby on Railsを基礎から学ぶ 第7回

〜ファインダーメソッドとSQL

 

今回はファインダーメソッドSQLをやります。

Railsでは、モデルとDBのテーブルががっちりくっついていてjavaVisual Studioのときのように、細かなSQLの整備や隠蔽が不要です。このあたりは大変すっきりしています。

また次回から始まるresourseの概念では、SQLを直接触らないRails特有のCRUD(追加・読込・更新・削除)の書き方に馴れなければなりません。

今回は内容的には結構暇ですがちゃんと見てくれるとありがたいです。

 

では、本題に入ります。

 

注意.Config\application.rbconfig.active_record.whitelist_attributes

 

えーと、今日はいきなりわき道的な話になりますが、表題の通り、configフォルダの中に、

application.rbという重要なファイルがあります。

このファイルは重要なファイルですが、英語がいっぱい書いてあり、初心者にはいまいちよくわからないと思うので酢。

モデルだけでなく、パッケージ全体の設定の肝であることがわかってきますので、今回はここで少しお時間をください。。

さて、このファイルの中に、

 

# config.active_record.whitelist_attributes = true

 

という項目があって、コメントアウトはされていますが、=trueが事前に設定されています。

これをfalseにするか、上のようにコメントアウトしておかないと以下のメッセージが消えません。

この行のコメントを外すと、レコード更新を行う際に、ハッシュ(listでの更新が行えない状態になります。

たとえば、nameを更新するついでにハッシュでadministarator権限などもいっしょに更新できるように追加し、権限を取得した上でアプリに悪さをするといった悪質なサイトへの攻撃が行えるというわけです。

この行がコメントされていないと、現在はエラーが出ることがあります。

いまはまだローカルでの練習ですし、ハッシュを使って、レコードを更新しないと不便ですので、、コメントした状態でよいです。

※もし、trueだったら、コメントアウトしておいてください。設定は章が進めば戻すことになります。

警告メッセージの発生した画面

 

また、railsコンソールを立ち上げたときに、以下のようなメッセージが出た場合も同様です。

SECURITY WARNING: No secret option provided to Rack::Session::Cookie.

This poses a security threat. It is strongly recommended that you

provide a secret to prevent exploits that may be possible from crafted

cookies. This will not be supported in future versions of Rack, and

future versions will even invalidate your existing user cookies.

 

ちょっと脅かしてしまいましたが、本題です。

便利なモデル操作の学習をしましょう。

 

1.Railsコンソールからテーブルを検索する

 Railsコンソールというのがあり、ここから、モデルを直接操作できます。

 

>rails console

 

または>rails cでもいけます。

下のように、irb(main):001:0>という形で、rubyコマンドプロンプトとは異なるコマンド待ち状態になります。

この状態で、あずは全部出してみましょう。

ruby 1.9.3p392 (2013-02-22) [i386-mingw32]

 

C:\Documents and Settings\Adimin>cd \dragon

 

C:\dragon>rails console

Loading development environment (Rails 4.0.0.rc1)

irb(main):001:0>

 

@Allメソッド(全件出力)

これは、該当のモデルの中身全部を出します。

 こんなメソッドを打つのは、出来立てのモデルか初心者くらいしかいません。

このようにずらずらと出てしまいます。

 

irb(main):001:0> Member.all

  [1m[36mMember Load (203.1ms)[0m  [1mSELECT "members".* FROM "members"[0m

ð      #<ActiveRecord::Relation [#<Member id: 1, number: 1, name: "Thomas", full_name: nil, email:

・・・(略)およそ30行くらい・・・

irb(main):002:0>

 

Aall.map(&:カラム名)

今度はテーブルの特定の項目の値、全てを取り出します。

たとえば、idだったらそんなすごい量にはならないかもしれませんが、

何万件もあるデータではやはり使うのはキツイですね^^

irb(main):002:0> Member.all.map(&:id)

  [1m[35mMember Load (0.0ms)[0m  SELECT "members".* FROM "members"

=> [1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 4]

 

 

Bfindメソッド

これはファインダーメソッドではありません

しかし、よく使う初歩的な検索メソッドです。その名前の通り、引数の値で、idを検索します。

黒字の部分がメソッドの実行と、出力されたデータの中身です。

irb(main):003:0> Member.find(3)

  [1m[36mMember Load (109.4ms)[0m  [1mSELECT "members".* FROM "members" WHERE "members"."id

" = $1 LIMIT 1[0m  [["id", 3]]

=> #<Member id: 3, number: 11, name: "Jiro", full_name: "鈴木 次郎", email: "Jiro@example.com",

 birthday: "1981-12-01", gender: 0, administrator: false, created_at: "2013-05-15 08:05:16", up

dated_at: "2013-05-15 08:05:16", phone: nil>

 

 

Cインスタンスの生成

 今使用しているのはrailsコンソールです。

 コマンドプロンプトでも、git bashでも、もちろんポスグレ(postgreSQL)のコマンド画面でもありません。

Railsなのです。

今まで打ってきたメソッドはそのままrailsプログラミングに使えます。

そこで、Memberモデルを実体化してハッシュ変数に格納してみます。

 

member = Member.find(3)

 

こうすることで、id=3の会員のデータをmemberという実体化した変数に格納できるのです。

このとき、idからadmin権限まで、全てハッシュの形で持っています。

このようにハッシュでレコードごとやりとりするのはたしかに危険かもしれません。

回避方法については、後述しますので、今はこのまま続けます。

このあとは、そのまま

 

member.email

 

のようにすることで、その項目だけ抜き出すなどが可能です。

下のリストは、今の過程を貼り付けたものです。

irb(main):005:0> member = Member.find(3)

  [1m[35mMember Load (0.0ms)[0m  SELECT "members".* FROM "members" WHERE "members"."id" = $1

 LIMIT 1  [["id", 3]]

=> #<Member id: 3, number: 11, name: "Jiro", full_name: "鈴木 次郎", email: "Jiro@example.com",

 birthday: "1981-12-01", gender: 0, administrator: false, created_at: "2013-05-15 08:05:16", up

dated_at: "2013-05-15 08:05:16", phone: nil>

irb(main):006:0> member.email

=> "Jiro@example.com"

 

 

D存在しないレコードへの対応

 検索したときに、そのidのレコードが無い場合、どうするのでしょうか?

find_by_idメソッドであればnilが返りますので、これを用いてそのあと条件分岐などをさせることができます。

(123)などもちろんまだありません。

下のようになります。

irb(main):007:0> Member.find_by_id(123)

  [1m[36mMember Load (0.0ms)[0m  [1mSELECT "members".* FROM "members" WHERE "members"."id"

= 123 LIMIT 1[0m

=> nil

 

 

2.ファインダーメソッドで検索する

 ようやく、ファインダーメソッドになりました。

 ファインダーメソッドはたくさんあります。

 概ね他の言語DBにつないで、ゴリゴリSQLを書くタイミングで

 

railsは便利で高度なメソッドが多数用意されているのです。

 

それがファインダーメソッドです。

 

 

@whereメソッド

これはそのまんまのSQLにおけるwhereのようなものです。

 

下のように範囲指定between)に書けば、number12から14のものを検索できます。

irb(main):008:0> Member.where(number:12..14)

  [1m[35mMember Load (0.0ms)[0m  SELECT "members".* FROM "members" WHERE ("members"."number"

 BETWEEN 12 AND 14)

=> #<ActiveRecord::Relation [#<Member id: 4, number: 12, name: "Hana", full_name: "高橋 花", em

ail: "Hana@example.com", birthday: "1981-12-01", gender: 1, administrator: false, created_at: "

2013-05-15 08:05:16", updated_at: "2013-05-15 08:05:16", phone: nil>, #<Member id: 5, number: 1

3, name: "John", full_name: "田中 太郎", email: "John@example.com", birthday: "1981-12-01", gen

der: 0, administrator: false, created_at: "2013-05-15 08:05:16", updated_at: "2013-05-15 08:05:

16", phone: nil>, #<Member id: 6, number: 14, name: "Mike", full_name: "佐藤 次郎", email: "Mik

e@example.com", birthday: "1981-12-01", gender: 0, administrator: false, created_at: "2013-05-1

5 08:05:16", updated_at: "2013-05-15 08:05:16", phone: nil>

 

Aoederメソッド

これも、ほぼ予想がつくと思いますが、並べ替えをしてくれます。

 

members = Member.where(gender:1).order("number DESC")

DESC:逆順(大きい数値から)、

ASC:正順(小さい数字からで、デフォルトはこっちなので、省略可能です)

 

いま、上のように打ってみました。

ここで、membersという複数形の変数を作って、gender:1(つまり女性)のレコードを、

ギルドナンバーがDESC(降順、逆順)で、並べ替えた結果を出力します。

この結果は、もちろん複数レコードが予想されますから、membersというハッシュに複数行入れてしまうわけです。

中盤にQUERY PLANというのが表示されます。これで、レコード処理の流れが見えます。

どうやら、4件のようですね。

Loading development environment (Rails 3.2.1)

irb(main):001:0> members = Member.where(gender:1).order("number DESC")

  [1m[36mMember Load (531.2ms)[0m  [1mSELECT "members".* FROM "members" WHER

nder" = 1 ORDER BY number DESC[0m

  [1m[35mEXPLAIN (31.2ms)[0m  EXPLAIN SELECT "members".* FROM "members" WHERE

der" = 1 ORDER BY number DESC

EXPLAIN for: SELECT "members".* FROM "members"  WHERE "members"."gender" = 1 ORD

SC

                           QUERY PLAN

-----------------------------------------------------------------

 Sort  (cost=10.39..10.39 rows=1 width=2097)

   Sort Key: number

   ->  Seq Scan on members  (cost=0.00..10.38 rows=1 width=2097)

         Filter: (gender = 1)

(4 rows)

 

=> [#<Member id: 12, number: 45, name: "sakachan", full_name: "坂城つかさ", emai

: "1994-02-01", gender: 1, administrator: false, created_at: "2013-05-29 08:19:1

 "2013-05-29 08:19:10", phone: nil>, #<Member id: 10, number: 18, name: "Mary",

藤 花子", email: "Mary@example.com", birthday: "1981-12-01", gender: 1, administ

reated_at: "2013-05-15 08:05:16", updated_at: "2013-05-15 08:05:16", phone: nil>

・・・(略)・・・

irb(main):002:0>

 

Bファインダの結果にfindをかける

以下のようなことが可能です。

先ほどのmembersという検索結果に対して、さらにfindを使って、検索をかけましょう。

 

members.find(12)

 

上記の検索結果から、id=12sakachanという人がいるようですので、それを直接指定して、検索してみましょう。

irb(main):002:0> members.find(12)

  [1m[36mMember Load (125.0ms)[0m  [1mSELECT "members".* FROM "members" WHERE "members"."ge

nder" = 1 AND "members"."id" = $1 ORDER BY number DESC LIMIT 1[0m  [["id", 12]]

=> #<Member id: 12, number: 45, name: "sakachan", full_name: "坂城つかさ", email: "", birthday:

 "1994-02-01", gender: 1, administrator: false, created_at: "2013-05-29 08:19:10", updated_at:

"2013-05-29 08:19:10", phone: nil>

irb(main):003:0>

 

 

 

このように、検索結果を一度格納して、さらに何度もファインダメソッドをかけられるところは

Railsの大変便利なところです。

このまま、membersに対して、SQLを使ってみることにしましょう。

 

3.SQLを使用した検索をする

既に気付いている方も多いと思いますが、モデルでの操作は、結果を出す前に、SQLを自動的に発行しているのです。

Railsはこのように、SQLを隠蔽する言語ではないようです。

皆さんの中にもSQLインジェクション攻撃という言葉を聞いたことがあると思いますが、

このようなデータベースの接続を伴うユーザ操作では、SQLに対してナイーブになってしまうのが実情です。

便利さと安全性の両方を実現させねばならない状況になってきつつあるrailsですが、

きっとこの苦難もうまく乗り切ってくれるでしょう。

 

品代に入って、SQLです。

SQLは一般的に広く使われており、DB設計書や詳細設計所などでも、SQLをそのまま列挙しているプロジェクトもあります。

また、SQLに馴れている人は、そのほうがいいと思うので、直接SQLを入力する方法を書きます。

 

members.find_by_sql("SQL”)

 

実際に打ったものが下のリストです。

ここでも、前の項で作ったmembersからSQLを実行しています。

irb(main):003:0> members.find_by_sql("select * from members where name = 'sakachan'")

  [1m[35mMember Load (93.8ms)[0m  select * from members where name = 'sakachan'

=> [#<Member id: 12, number: 45, name: "sakachan", full_name: "坂城つかさ", email: "", birthday

: "1994-02-01", gender: 1, administrator: false, created_at: "2013-05-29 08:19:10", updated_at:

 "2013-05-29 08:19:10", phone: nil>]

irb(main):004:0>

 

 

 

ということで、おしまいです。

ファインダメソッドは、モデルのActiveRecordというクラスに入っています。

調べてみると、まとまりの良いサイトが少ないので、このページで少しずつ増やして生けたら十もいます。

Rails4.0では、レコードの更新に権限が必要となったようです。

このあたりもお伝えしていければと思います。

 

次回は、いよいよresourseに入ります。

今回はいまいちぱっとしなかった方も今はそれでいいです。

また戻って必要なときに見返してください。

 

 

     参考テキスト■

サ改定新版 基礎 Ruby on Rails オイアクス監修 インプレスジャパン

http://www.oiax.jp/rails3book

     参考サイト■

 

 

戻るとっぷ次へ

 

Raccoon's homepage Copyright (C) 2000-2013 あらいベアー