Rails4のActiveRecordでDBからデータを読み出すサンプル集
[履歴] [最終更新] (2015/07/05 05:26:13)

概要

Rails4にはActiveRecordが実装されています。ここでは特にデータの読み出しに関するサンプルを見てみます。

事前準備

$ rails new myApp
$ rails generate scaffold myModel field1:string field2:integer field3:date field4:boolean
$ rake db:migrate

サンプル集

app/models/my_model.rb

class MyModel < ActiveRecord::Base
  scope :my_named_scope1, -> { where(field1: 1) }
  scope :my_named_scope2, -> { order(id: :desc) }
  scope :my_named_scope3, -> { my_named_scope2.limit(20) }
  scope :my_named_scope4, -> (val) {
    where(field1: val)
  }
  # default_scope { order(id: :desc) }
end

app/controllers/my_models_controller.rb
(いずれも、SQL文はコンソールに出力されます)

class MyModelsController < ApplicationController
  before_action :set_my_model, only: [:show, :edit, :update, :destroy]

  # GET /my_models
  # GET /my_models.json
  def index
    # すべてを取得
    # @my_models = MyModel.all

    # プライマリキーで検索
    # @my_models = [MyModel.find(1)]
    # @my_models = MyModel.find([1,2])
    #   ↑SELECT "my_models".* FROM "my_models" WHERE "my_models"."id" IN (1, 2)
    # @my_models = MyModel.find(1,2)

    # 特定の列で検索
    # @my_models = [MyModel.find_by(field1: 1)]
    #   ↑SELECT "my_models".* FROM "my_models" WHERE "my_models"."field1" = 1 LIMIT 1
    # @my_models = [MyModel.find_by(field1: 1, field2: nil)]

    # 検索結果の最初または最後
    # @my_models = [MyModel.all.order(id: :desc).first, MyModel.all.order(id: :desc).last]

    # 空の結果を返す
    # @my_models = MyModel.none


    ### ↓メソッドチェーンが行われる場合、SQL文の発行は最後まで遅延されます

    # WHERE句
    # @my_models = MyModel.where(field3: '2014-01-01'..'2015-01-01', field2: nil).order(field1: :desc, field3: :asc)
    #   ↑AND,BETWEEN,ORDER
    #   ↑SELECT "my_models".* FROM "my_models" WHERE ("my_models"."field3" BETWEEN '2014-01-01' AND
    #       '2015-01-01') AND "my_models"."field2" IS NULL ORDER BY "my_models"."field1" DESC
    # @my_models = MyModel.where(field1: [1,2]) #IN
    #   ↑SELECT "my_models".* FROM "my_models" WHERE "my_models"."field1" IN (1, 2)

    # プレイスホルダ
    # @my_models = MyModel.where('field1 = ? AND field2 IS ?', 1, nil)
    # @my_models = MyModel.where('field1 = :val1 AND field2 IS :val2', val1: 1, val2: nil)

    # 否定
    # @my_models = MyModel.where.not('field1 = :val1 AND field2 IS :val2', val1: 1, val2:  nil) 
    #   ↑SELECT "my_models".* FROM "my_models" WHERE (NOT (field1 = 1 AND field2 IS NULL))

    # 不要な列は取得しない (メモリの節約)
    # @my_models = MyModel.where.not('field1 = :val1 AND field2 IS :val2', val1: 1, val2:  nil)
    #   .select(:field1, :field2, :field3, :field4, :id)

    # 途中のいくつかを取得 (ページャなどで利用)
    # @my_models = MyModel.order(field1: :desc).limit(1).offset(1)

    # よく使用するスコープはモデルファイルに記述しておく
    # @my_models = MyModel.my_named_scope2
    # @my_models = MyModel.my_named_scope4(1)

    # SQLを直接発行 (非推奨)
    # @my_models = MyModel.find_by_sql('SELECT * from "my_models"')
    # @my_models = MyModel.find_by_sql(['SELECT * from "my_models" where field1 = ?', 2])

    # デフォルトスコープ
    # render text: MyModel.all
    #   ↑SELECT "my_models".* FROM "my_models" ORDER BY "my_models"."id" DESC
    # render text: MyModel.unscoped.all
    #   ↑SELECT "my_models".* FROM "my_models ← デフォルトスコープを一時的に解除

    # SQLレベルで重複を排除 (取得後のuniqではない)
    # render text: MyModel.select(:field1).distinct.count

    # 存在確認 (whereのあとに記述)
    # render text: MyModel.where(field1: 2).exists?
    #   ↑SELECT 1 AS one FROM "my_models" WHERE "my_models"."field1" = 2 LIMIT 1

    # 個数
    # render text: MyModel.all.count
    #   ↑SELECT COUNT(*) FROM "my_models"

    # 基本的な統計処理
    # render text: MyModel.all.sum(:field1)
    # render text: MyModel.all.average(:field1)
    # render text: MyModel.all.maximum(:field1)
    # render text: MyModel.all.minimum(:field1)

    # GROUP BY:
    # $ rails runner 'p MyModel.select("field4, AVG(field1) AS avg_field1").group(:field4).first.avg_field1'

    # GROUP_BY and HAVING
    # $ rails runner 'p MyModel.select("field4, AVG(field1) AS avg_field1").group(:field4)
    #   .having("avg_field1 = ?", 2).first.avg_field1'

    # 無名配列化
    # render text: MyModel.all.pluck(:field1)
    #   ↑SELECT "my_models"."field1" FROM "my_models"
    # render text: MyModel.all.select(:field1).map{|model| model.field1} #これも同じ意味
    # render text: MyModel.all.pluck(:field1, :field2) #複数の列を指定可能
    #  → 例: [["1", nil], ["2", nil], ["3", nil]]
  end

なお、上記サンプル中にもあるように、"rails runner" を利用することで、cronで実行するためのバッチファイルを作成したり、デバッグに役立てることができます。

$ rails runner 'p MyModel.find(1)'

出力例

#<MyModel id: 1, field1: "1", field2: nil, field3: "2014-06-15", field4: false, created_at: "2014-06-15 23:05:36",
 updated_at: "2014-06-15 23:05:36">

アセットパイプラインの検索パスを調査する例

$ rails runner 'p Rails.application.assets.paths'
["/vagrant/app/assets/images", "/vagrant/app/assets/javascripts",
 "/vagrant/app/assets/stylesheets", "/vagrant/vendor/assets/javascripts",
 "/vagrant/vendor/assets/stylesheets"]

以下の操作と同等の情報を得られます。

$ rails c
[1] pry(main)> puts Rails.application.assets.paths
/vagrant/app/assets/images
/vagrant/app/assets/javascripts
/vagrant/app/assets/stylesheets
/vagrant/vendor/assets/javascripts
/vagrant/vendor/assets/stylesheets
=> nil
関連ページ