【Rails:3】データベースとモデル

データベースとモデル

  • リレーショナルデータベース
データベース モデルオブジェクト
レコード(行) ひとつのデータ
フィールド(列) データの属性
テーブル レコード×フィールドの集合
    • ORM(Object-Relational Mapping)
      データベースのレコードとオブジェクトを紐付けて直感的に扱う仕組み。
    • DBMS(データベース管理システム)
      MySQL, PostgreSQL
    • DBMSとネットワーク
      多くのDBMSはネットワーク(TCP/IP)に対応していますので、アプリケーションとデータベースを別のサーバに設置し、ネットワークを通じてやりとりさせることもできます。
  • モデル
    モデルは、データベースのテーブルに対応するクラスです。
    モデルクラスのインスタンスは、ひとつのレコードを表すオブジェクトです。
    Railsのモデルは、sql文を生成してくれるfindメソッド等を使って、レコードを直感的に扱うことができます。
    • 主キー
      レコードを識別するID番号(重複不可)。
データベース(テーブル名) hoge_fugas
モデル(クラス名) HogeFuga
モデル(ファイル名) hoge_fuga.rb
単体テスト(ファイル名) hoge_fuga_test.rb
  • データベース作成
    DBMSへの接続設定
    config\database.ymlで設定します。
development:                   # 開発用データベースの設定
  adapter: mysql                  # DBMS名
  database: appname_development   # データベース名
  username: root                  # DBMSのユーザ名
  password:                       # DBMSのパスワード
  host: localhost                 # DBMSのホスト名/IPアドレス
  encording: utf8                 # データベースの文字コード

test:                          # テスト用データベースの設定
  adapter: mysql
  database: appname_test
  username: root
  password: 
  host: localhost
  encording: utf8

production:                    # 本番用データベースの設定
  adapter: mysql
  database: appname_production
  username: hoge
  password: fuga
  host: hoge.fuga.piyo.jp 
  encording: utf8
  • MySQLのデフォルト文字コード設定
    MySQLの設定ファイルに文字コードを定義すれば、config\database.ymlでの"encording: utf8"や、データベース作成時の"character set utf8"が不要になります。
# SERVER SECTION

[mysqld]
default-character-set=utf8
skip-character-set-client-handshake
($ NET STOP mysql)
  $ NET START mysql
mysql> show variables like "char%";

テーブル作成

  • モデル作成
$ ruby script\generate model mdlname
  • マイグレーション
    スクリプトを利用して、テーブルの作成や変更を容易に行う仕組み。
    マイグレーションスクリプトのファイル名には通し番号がついており、この番号をもとにバージョン管理されます。
    バージョンアップ時はupメソッドが、バージョンダウン時はdownメソッドが実行されます。
    • up/downメソッドの中に記述できるメソッドの例
メソッド 機能
create_table/drop_table テーブルの作成/削除
add_column/remove_column フィールドの追加/削除
rename_column フィールド名の変更
change_column フィールドの型の変更
add_index/remove_index インデックスの作成/削除(後述)
$ rake db:migrate
# マイグレーションを最新バージョンまで適用(開発用データベース)

$ rake db:migrate RAILS_ENV=production
# マイグレーションを最新バージョンまで適用(本番用データベース)

$ rake:db:migrate VERSION=***
#マイグレーションを***番までバージョンアップ/ダウン

マイグレーションのバージョンは、データベース内に作成されるschema_infoテーブルで管理されています。
バージョン番号は、スクリプトの処理が正常に実行されたかどうかに関わらず変更されてしまいますので、スクリプトの記述にミス等があると、バージョン変更ができなくなってしまうことがあります。
その場合は、一度データベースを削除してから再作成し、スクリプトを修正して、再度rake db:migrateを実行しましょう。

    • インデックス
      フィールドにインデックスを作成すると、検索やソートを高速化できます。
      レコードの保存のたび、そのフィールドを基準にソートされるからです。
      各レコードで値の一意性が高いフィールドに作成すると有効です。

フィクスチャ

  • フィクスチャ
    データベースの初期状態。
    フィクスチャファイルに記述した内容は、いつでもデータベースのデータとして利用することができます。
    ただし、フィクスチャの内容がデータベースの状態と矛盾していると、エラーが発生します。
hoge:
  id: 1
  name: Mr.Hoge
  age: 25

fuga:
  id: 2
  name: Ms.Fuga
  age: 24piyo:
  id: 30
  name: Mr.Piyo
  age: 20
  • scaffold
    scaffoldという仕組みを利用すると、一般的にテーブルの管理で必要とされる機能を一気に作成できます。
    ただし、あくまでも開発をサポートするための仕組みであり、scaffoldを利用しなくても開発は可能です。
    アプリケーションの仕様に沿って、柔軟に開発していくことが重要です。
$ ruby script\generate scaffold mdlname ctrname
# mdlnameモデル作成
# mdlnameモデルを管理するctrnameコントローラ作成
# ctrnameコントローラにlist, show, new, create, edit, update, destroyアクション作成
# (list, show, new, editはテンプレートも作成)

findメソッド

findメソッドの基本形はMdlName.find(条件)です。

  • ひとつのレコードを抽出(idで検索)
@member = Member.find(1)   #=> @hoge
@nanme = @member.name      #=> "Mr.Hoge"
  • すべてのレコードを抽出(:allオプション)
@members = Member.find(:all)   #=> [@hoge, @fuga, ..., @piyo]
  • その他の条件指定
    • 検索(:conditionsオプション)
his_name = "Mr.Hoge"
his_age = 25

@members = MdlName.find(:all,
                        :conditions => ["name = ? and age = ?", his_name, his_age])   #=> [@hoge]
@members = MdlName.MdlName.find_by_name_and_age(his_name, his_age)   #=> [@hoge]
    • ソート(:orderオプション)
@members = MdlName.find(:all,
                        :order => "age")   #=> [@piyo, ..., @fuga, @hoge]

@members = MdlName.find(:all,
                        :order => "age DESC")   #=> [@hoge, @fuga, ..., @piyo]
      • all の代わりに first/last を使えば、配列のうち最初/最後のレコードだけ取り出すことができます。
      • :conditions => "name = #{name} and age = #{age}" としてはならない理由
        ユーザにブラウザのフォームから文字列を入力させる際、SQLインジェクション(危険なSQL文を実行させるようなサーバへの攻撃)の恐れがあります。
        ユーザが入力した情報を変数に取得する場合、情報に危険な文字列が含まれていると、それがSQL文として実行されてしまい大変危険です。
        「?」を利用して記述すれば、情報をエスケープ(ユーザが入力した情報がそのままSQL文として実行されないようにする処理)してくれます。
    • 個数制限(:limit/:offsetオプション)
@members = MdlName.find(:all,
                        :limit => 5,
                        :offset => 10)   #=> 10〜14番目のレコードの配列
  • sql文を直接指定(find_by_sql
    findメソッドに用意されているオプションだけでは複雑すぎる条件などに有効です。
his_name = "Mr.Hoge"
his_age = 25

@members = MdlName.find_by_sql(["select * from members where name = ? and age = ?", his_name, his_age])   #=> [@hoge]
  • find以外のメソッド
メソッド 機能
average(フィールド名) 平均
count レコード数
maximum(フィールド名) 最大値
minimum(フィールド名) 最小値
sum(フィールド名) 合計