Railsドキュメント

モデル(model)

モデルについて

説明

モデルとはアプリケーションが扱うデータや処理を表現する仕組みのこと

モデル名とテーブル名の規約

  • 英大文字から始まる
  • 英数字のみ
  • 単語の区切りでは、先頭文字を大文字
  • 単数形の名詞
    • Entry
    • UserComment
  • ファイルはapp/modelsディレクトリに格納
  • ファイル名は、モデル名の単語区切りを「_」にし、すべて小文字にしたもの
    • app/models/entry.rb
    • app/models/user_comment.rb

命名規則

種類 説明
モデル名 先頭は大文字で単数形 User
モデルのファイル名 先頭は小文字で単数形 user.rb
テーブル名 先頭は小文字で複数形 users
テストスクリプト名 xxx_test.rb user_test.rb

モデルを生成

説明

モデルオブジェクトを生成
保存はまだされていないため、saveメソッドなどを使って保存
生成と同時に保存したい場合は、createメソッドを使用

使い方

モデル.new(属性={})

モデルオブジェクトを生成

User.new

属性を指定

User.new(name: "DHH")

複数属性を指定

User.new(name: 'bob', age: '18')

ブロックで指定

users.new do |user|
  user.name = 'Oscar'
end

ソースコード

モデルを生成して保存

説明

モデルを生成して保存
生成のみしたい場合は、newメソッドを使用

使い方

モデル.create(属性=nil, ブロック引数)

# 失敗したら例外発生
モデル.create!(属性=nil, ブロック引数)

モデルを生成して保存

User.create(name: "TestUser", profile: "profile")

whereしてcreate

users = User.where(name: 'Oscar')
users.create

属性ハッシュの配列を保存

User.create([{name: "A"},{name: "B"}])

ブロック

User.create do |user|
    User.name = 'tenderlove'
end

ソースコード

IDを指定してレコードを取得

説明

IDを指定してレコードを取得
指定したIDの中に存在しないIDが1つでもあると例外が発生

使い方

モデル.find(件数..)

IDを指定してレコードを取得

Person.find(1)

複数IDを指定

Person.find(1, 2, 6)

配列で指定

Person.find([7, 17])

ソースコード

条件を指定して最初の1件を取得

説明

条件を指定して最初の1件を取得
存在しない場合はnil

使い方

モデル.find_by(条件..)

# 失敗したら例外発生
モデル.find_by!(条件..)

category_idが1の最初の値を取得

Page.find_by category_id: 1
# SQL: SELECT "pages".* FROM "pages" WHERE "pages"."category_id" = 1 LIMIT 1

category_idの1が存在しない場合

Page.find_by category_id: 1
# nil

複数条件を指定

Page.find_by category_id: 1, user_if: 1

whereで書き換える

# Page.find_by category_id: 1
Page.where(category_id: 1).take

ソースコード

条件を指定してレコードが1個しかない場合だけ取得

説明

条件を指定してレコードが1個しかない場合だけ取得

使い方

モデル.find_sole_by(条件..)

Product.find_sole_by(["price = %?", price])

ソースコード

分割してレコードを取得して1件ずつ処理

説明

分割してレコードを取得して1件ずつ処理
デフォルトでは1000件ずつ処理 大きなデータをもつモデルなどを処理する時に使う

使い方

モデル.find_each(start: 開始(nil), finish: 終了(nil), batch_size: バッチサイズ(1000), error_on_ignore: ErrorIgnore(nil), order: 並び順=:asc, ブロック引数)

オプション

オプション 説明 デフォルト値
:batch_size 同時処理数 1000
:start 処理開始位置  
:finish 終了するときのキー  
:error_on_ignore 例外を発生させる  
:order 主キーの順序を指定 :asc

category_idが1の値をeachする

Person.find_each do |person|
  person.do_awesome_stuff
end

同時処理数を10

Post.find_each(:batch_size => 10) do |post|
end

開始位置を指定

Person.find_each(start: 2000, batch_size: 2000) do |person|
end

補足

  • find_in_batchesとの違いは1件ずつ処理すること

ソースコード

分割してレコードを取得して処理

説明

分割してレコードを取得して処理
デフォルトで1000件ずつ処理

使い方

モデル.in_batches(of: バッチのサイズ=1000, start: 開始する主キーの値=nil, finish: 終了する主キーの値=nil, load: リレーションをロードするか=false, error_on_ignore: エラーを発生させるか=nil, order: 主キーの順序=:asc)

オプション

オプション 説明 デフォルト値
:of バッチのサイズ 1000
:load リレーションをロードするか false
:start 処理開始位置  
:finish 終了するときのキー  
:error_on_ignore 例外を発生させる  
:order 主キーの順序を指定 :asc

分割してレコードを取得して処理

Person.where("age > 21").in_batches do |relation|
  relation.delete_all
  sleep(10) # 削除クエリの調整
end

補足

  • 処理する順序は指定できない
  • find_eachとの違いは配列で値を受けとること
  • 順番に処理したい場合は、find_eachを使用

ソースコード

条件を指定して初めの1件を取得し1件もなければ作成

説明

条件を指定して初めの1件を取得し1件もなければ作成

使い方

モデル.find_or_create_by(条件, ブロック引数)

# 失敗したら例外発生
モデル.find_or_create_by!(条件, ブロック引数)

存在しないので新しく作成

User.find_or_create_by(first_name: "Taraou")
#=> <User id: 1, first_name: "Tarou", last_name: nil>

既に存在する場合は既存のレコードを取得

User.find_or_create_by(first_name: "Taraou")
#=> <User id: 1, first_name: "Taraou", last_name: nil>

ブロック指定

User.find_or_create_by(first_name: "Scarlett") do |user|
  user.last_name = "Johansson"
end
#=> <User id: 2, first_name: "Scarlett", last_name: "Johansson">

補足

  • find_or_create_byとの違いは、作成する時に呼ぶメソッドがnewではなくcreate

ソースコード

データベースのユニーク制約を使って作成できなければ初めの1件を取得

説明

データベースのユニーク制約を使って作成、できなければ初めの1件を取得
find_or_create_byでは作成されるまでに別プロセスによって作成されている可能性があったので、その問題を解決した処理
create_or_find_by!はエラーの時に例外が発生

使い方

create_or_find_by(属性, ブロック引数)

# 失敗したら例外発生
f.create_or_find_by(属性, ブロック引数)

データベースのユニーク制約を使って作成、できなければ初めの1件を取得

User.create_or_find_by(first_name: 'Penélope')

失敗したら例外発生

User.create_or_find_by!(first_name: 'Penélope')

ソースコード

条件を指定して初めの1件を取得し1件もなければ作成

説明

条件を指定して初めの1件を取得し1件もなければ作成

使い方

モデル.find_or_initialize_by(条件, ブロック引数)

# 失敗したら例外発生
モデル.find_or_initialize_by!(条件, ブロック引数)

存在しないので新しく作成

User.find_or_initialize_by(first_name: 'Penélope')

既に存在する場合は既存のレコードを取得

User.find_or_initialize_by(first_name: 'Penélope')

ブロック指定

User.find_or_initialize_by(first_name: 'Scarlett') do |user|
  user.last_name = "O'Hara"
end

補足

  • find_or_create_byとの違いは、作成する時に呼ぶメソッドがcreateではなくnew

ソースコード

有効期限付きの署名されたIDからレコードを取得

説明

有効期限付きの署名されたIDからレコードを取得

使い方

モデル.find_signed(署名されたID, purpose: nil)

# 失敗したら例外発生
モデル.find_signed!(署名されたID, purpose: nil)

signed_id = User.first.signed_id expires_in: 15.minutes, purpose: :password_reset

User.find_signed signed_id #=> nil, since the purpose does not match

travel 16.minutes
User.find_signed signed_id, purpose: :password_reset #=> nil, since the signed id has expired

travel_back
User.find_signed signed_id, purpose: :password_reset #=> User.first

ソースコード

SQLを直接指定して取得

説明

SQL文を直接書いて取得

使い方

モデル.find_by_sql(SQL文, バインド=[], preparable: プレパラート=nil, ブロック引数)

SQL文を直接書いて取得

Page.find_by_sql("SELECT * FROM pages LIMIT 10")
# SELECT * FROM pages LIMIT 10

変数でSQL文

query = "SELECT * FROM pages LIMIT 10"
Page.find_by_sql(query)

ソースコード

SQLを直接指定して取得

説明

SQL文を直接書いて取得

使い方

select_all(SQL文, 名前=nil, バインド=[], プレパラート=nil, async: Async=false)

Client.connection.select_all("SELECT first_name, created_at FROM clients WHERE id = '1'").to_hash
# [
#   {"first_name"=>"Rafael", "created_at"=>"2012-11-10 23:23:45.281189"},
#   {"first_name"=>"Eileen", "created_at"=>"2013-12-09 11:22:35.221282"}
# ]

補足

  • find_by_sqlとの違いは取得したオブジェクトのインスタンス化は行わないこと

ソースコード

すべてのレコードを取得

説明

すべてのレコードを取得

使い方

モデル.all

Page.all
# SELECT "pages".* FROM "pages"

ソースコード

先頭のレコードを取得

説明

モデルの先頭のレコードを取得

使い方

モデル.first(件数=nil)

# 失敗したら例外発生
モデル.first!(件数=nil)

モデルの先頭のレコードを取得

Page.first
# SQL: SELECT "pages".* FROM "pages" LIMIT 1

複数件数を取得

Page.first(3)
# SQL: SELECT "pages".* FROM "pages" LIMIT 3

orderで並び替え後のレコードを取得

Page.order(:title).first
# SQL: SELECT * FROM pages ORDER BY pages.title ASC LIMIT 1

モデルが空の場合

Page.first
# nil

ソースコード

2番目のレコードを取得

説明

2番目のレコードを検索

使い方

モデル.second()

# 失敗したら例外発生
モデル.second!()

Person.second # returns the second object fetched by SELECT * FROM people
Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
Person.where(["user_name = :u", { u: user_name }]).second

ソースコード

3番目のレコードを取得

説明

3番目のレコードを検索

使い方

モデル.third()

# 失敗したら例外発生
モデル.third!()

Person.third # returns the third object fetched by SELECT * FROM people
Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
Person.where(["user_name = :u", { u: user_name }]).third

ソースコード

4番目のレコードを取得

説明

4番目のレコードを検索

使い方

モデル.fourth()

# 失敗したら例外発生
モデル.fourth!()

Person.fourth # returns the fourth object fetched by SELECT * FROM people
Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
Person.where(["user_name = :u", { u: user_name }]).fourth

ソースコード

5番目のレコードを取得

説明

5番目のレコードを検索

使い方

モデル.fifth()

# 失敗したら例外発生
モデル.fifth!()

Person.fifth # returns the fifth object fetched by SELECT * FROM people
Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
Person.where(["user_name = :u", { u: user_name }]).fifth

ソースコード

最後のレコードを取得

説明

モデルの最後のレコードを取得

使い方

モデル.last(件数=nil)

# 失敗したら例外発生
モデル.last!(件数=nil)

pagesテーブルの最後のレコードを取得

Page.last
# SQL: SELECT "pages".* FROM "pages" ORDER BY "pages"."id" DESC LIMIT 1

複数を取得

Page.last(2)
# SELECT "pages".* FROM "pages" ORDER BY "pages"."id" DESC LIMIT 2

orderで並び替え後のレコードを取得

Page.order(:title).last
# SQL: SELECT * FROM pages ORDER BY pages.title DESC LIMIT 1

モデルが空の場合

Page.last
# nil

ソースコード

最後から2番目のレコードを取得

説明

最後から2番目のレコードを取得

使い方

モデル.second_to_last()

# 失敗したら例外発生
モデル.second_to_last!()

Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
Person.where(["user_name = :u", { u: user_name }]).second_to_last

ソースコード

最後から3番目のレコードを取得

説明

最後から3番目のレコードを取得

使い方

モデル.third_to_last()

# 失敗したら例外発生
モデル.third_to_last!()

Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
Person.where(["user_name = :u", { u: user_name }]).third_to_last

ソースコード

指定した数のレコードを取得

説明

引数で指定した件数のレコードを取得
取得するレコードをIDなどで指定することはできない

使い方

モデル.take(件数=nil)

# 失敗したら例外発生
モデル.take!(件数=nil)

1件のレコードを取得

Person.take
# SQL: SELECT * FROM people LIMIT 1

2件のレコードを取得

Person.take(2)
# SQL: SELECT * FROM people LIMIT 2

whereの後に使用

Person.where(["name LIKE '%?'", name]).take

失敗したら例外発生

Person.take!

ソースコード

カラムを指定して取得

説明

カラムを指定して取得

使い方

モデル.select(カラム名..)

カラムを指定して取得

person.pets.select(:id, :name)

ブロックで指定

person.pets.select do |pet|
  pet.name =~ /oo/
end

ソースコード

条件に当てはまる値を全て取得

説明

条件に当てはまるレコードを全て取得

使い方

モデル.where(条件..)

文字列で指定

Page.where("category_id = '1'")
# SELECT "pages".* FROM "pages" WHERE "pages"."category_id" = 1

ハッシュで指定

Page.where(category_id: 1)
# SELECT "pages".* FROM "pages" WHERE "pages"."category_id" = 1

配列で指定

Page.where(["category_id = ? and url_id = ?", 1, 1])
# SELECT "pages".* FROM "pages" WHERE (category_id = 1 and url_id = 1)

プレースホルダを使用

Page.where("category_id = :category_id", {category_id: params[:category_id]})

複数キーを指定

Page.where(category_id: [1, 2])
# SELECT * FROM pages WHERE (pages.category_id IN (1,2))

NULLのすべてのデータを取得

Page.where(title: nil)

NOT条件

Page.not.where category_id: 1
# SELECT * FROM pages WHERE (pages.category_id != 1)

AND条件

Page.where(category_id: 1).where(user_id: 1)

OR条件

Page.where(category_id: 1).or(Page.where(user_id: 1))

範囲指定

User.where(:id => 1..10)

正規表現

User.where("name like '%hoge%'")

1年

User.where(created_at: '2020-1-5'.in_time_zone.all_year)

1月

User.where(created_at: '2020-1-5'.in_time_zone.all_month)

1日

User.where(created_at: '2020-1-5'.in_time_zone.all_day)

終端なしの範囲指定

User.where(age: 20..)
# SELECT * FROM users WHERE (users.age >= 20)

補足

  • 文字列で指定する場合は、SQLインジェクションの脆弱性が発生する可能性があるので、外部入力されるキーに関しては配列やプレースホルダを使用してください

ソースコード

既存のwhere条件を上書き

説明

既存のwhere条件を上書き
上書きされるのは指定したキーのみで、指定されていないwhereの条件はそのまま

使い方

モデル.rewhere(条件)

既存のwhere条件を上書き

Post.where(trashed: true).rewhere(trashed: false)
# WHERE `trashed` = 0

指定したキーのみ上書き

Post.where(active: true).where(trashed: true).rewhere(trashed: false)
# WHERE `active` = 1 AND `trashed` = 0

ソースコード

where句全体を反転させて取得

説明

where句全体を反転させて取得

使い方

where句.invert_where()

where句全体を反転させて取得

User.where(accepted: true)
# WHERE `accepted` = 1

User.where(accepted: true).invert_where
# WHERE `accepted` != 1

scopeを指定

class User
    scope :active, -> { where(accepted: true, locked: false) }
end

User.active
# WHERE `accepted` = 1 AND `locked` = 0

User.active.invert_where
# WHERE NOT (`accepted` = 1 AND `locked` = 0)

ソースコード

WHERE条件のハッシュを取得

説明

WHERE条件のハッシュを取得

使い方

モデル.where_values_hash(テーブル名=klass.table_name)

User.where(name: 'Oscar').where_values_hash
#=> {name: "Oscar"}

ソースコード

条件式に一致しないものを取得

説明

WHEREと一緒に使用し条件式に一致しないものを取得

使い方

モデル.where.not(条件..)

条件式に一致しないものを取得

User.where.not(name: "Jon")
# SELECT * FROM users WHERE name != 'Jon'

nilを指定

User.where.not(name: nil)
# SELECT * FROM users WHERE name IS NOT NULL

配列を指定

User.where.not(name: %w(Ko1 Nobu))
# SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')

複数条件

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'

ソースコード

2つのリレーションをANDで繋ぐ

説明

2つのリレーションをANDで繋ぐ

使い方

モデルのリレーション.and(他のモデルのリレーション)

Post.where(id: [1, 2]).and(Post.where(id: [2, 3]))
# SELECT `posts`.* FROM `posts` WHERE `posts`.`id` IN (1, 2) AND `posts`.`id` IN (2, 3)

ソースコード

OR条件式

説明

OR条件式

使い方

モデル.or(条件式)

A or B

Post.where("id = 1").or(Post.where("author_id = 3"))
# SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))

A, B or C

Post.where(id: 1, name: "名前").or(Post.where(age: nil))
# SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 1 AND "posts"."name" =  "名前" OR "posts"."age" IS NULL)

A or B, C

Post.where(id: 1).or(Post.where(name: "名前", age: nil))
# SELECT "posts".* FROM "posts" WHERE ("posts"."id" = 1 OR "posts"."name" =  "名前" AND "posts"."age" IS NULL)

ソースコード

取得した値を並び替え

説明

取得したレコードを特定のキーで並び替える

使い方

モデル.order(引数..)

並び順

並び順 説明
ASC 小さい方から大きい方に並ぶ(昇順)
DESC 大きい方から小さい方に並ぶ(降順)

pagesテーブルをcategory_idで並び替える

Page.order(:category_id)
# SELECT "pages".* FROM "pages" ORDER BY category_id

昇順で並び替える

Page.order(:category_id :asc)
# SELECT "pages".* FROM "pages" ORDER BY category_id ASC

文字列で指定

Page.order("category_id ASC")
# SELECT "pages".* FROM "pages" ORDER BY category_id ASC

複数指定

User.order(:name, email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC

ソースコード

既存の並び順を指定した条件に置き換える

説明

既存の並び順を指定した条件に置き換える

使い方

モデル.reorder(ソート式..)

並び順

並び順 説明
ASC 小さい方から大きい方に並ぶ(昇順)
DESC 大きい方から小さい方に並ぶ(降順)

既存の並び順を指定した条件に置き換える

User.order('email DESC').reorder('id ASC')

reorderより後の並び順は追加

User.order('email DESC').reorder('id ASC').order('name ASC')

ソースコード

取得した値を逆順に並び替え

説明

取得した値を特定のキーで逆順に並び替える

使い方

モデル.reverse_order()

Page.order("category_id").reverse_order

ソースコード

重複のない値を取得

説明

重複のない値を取得

使い方

モデル.distinct(重複を削除するか=true)

重複のない値を取得

User.select(:name).distinct

重複も削除

User.select(:name).distinct(false)

ソースコード

レコードが1個しかない場合だけ取得

説明

レコードが1個しかない場合だけ取得

使い方

モデル.sole()

sole()

ソースコード

空のモデルを取得

説明

空のモデルオブジェクトを取得

使い方

モデル.none()

空のモデルオブジェクトを取得

Post.none

ソースコード

取得するレコード数の上限を指定

説明

取得するレコード数の上限を指定

使い方

モデル.limit(最大取得行数)

usersテーブルから最大5件を取得

User.limit(5)
# SELECT * FROM users LIMIT 5

重複する場合あとが優先(最大20件取得)

User.limit(5).limit(20)
# SELECT * FROM users LIMIT 20

途中から指定した件数を取得

User.limit(5).offset(30)
# SELECT * FROM users LIMIT 5 OFFSET 30

ソースコード

特定のレコード位置から取得

説明

特定のレコード位置から取得

使い方

モデル.offset(取得開始位置)

usersテーブルの5件目以降を取得

Page.offset(5)
# SELECT "pages".* FROM "pages" LIMIT -1 OFFSET 5

途中から指定した件数を取得

User.limit(5).offset(30)
# SELECT * FROM users LIMIT 5 OFFSET 30

ソースコード

読み込み専用で取得

説明

読み込み専用としてモデルを取得

使い方

モデル.readonly(値=true)

# 失敗したら例外発生
モデル.readonly!(値=true)

users = User.readonly
users.first.save
# ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord

ソースコード

複数のテーブルを結合して取得

説明

複数のテーブルを結合して取得

使い方

モデル.joins(条件..)

categoryテーブルにpostテーブルを結合して取得

Category.joins(:posts)
# SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id

2個のテーブルをjoin

Post.joins(:category, :comments)
# SELECT posts.* FROM posts INNER JOIN categories ON posts.category_id = categories.id INNER JOIN comments ON comments.post_id = posts.id

ネストして結合

Page.joins(categories: :element)
#  SELECT "pages".* FROM "pages" INNER JOIN "pages_categories" ON "pages_categories"."page_id" = "pages"."id" INNER JOIN "categories" ON "categories"."id" = "pages_categories"."category_id" INNER JOIN "elements" ON "elements"."id" = "categories"."element_id"

SQLで記述

User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
# SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id

ソースコード

関連するモデルの左外部結合

説明

関連するモデルの左外部結合

使い方

モデル.left_outer_joins(モデル名..)

User.left_outer_joins(:posts)
# SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

ソースコード

関連するテーブルをまとめて取得

説明

関連するテーブルをまとめて取得

使い方

モデル.includes(引数..)

関連するテーブルをまとめて取得

Page.includes(:category)
# SELECT "pages".* FROM "pages"
# SELECT "categories".* FROM "categories" WHERE "categories"."id" IN (1)

複数指定

User.includes(:address, :friends)

孫モデルをまとめる

User.includes(friends: :address)

子モデルが存在するレコードを取得

User.joins(:friends).where("friends.id IS NOT NULL")

孫モデルが存在するレコードを取得

User.friends.joins(:followers).where("followers.id IS NOT NULL")

OR演算子を指定

User.where(address: 'hoge').or(User.where(friends: 'fuga'))

ソースコード

引数のプリロード

説明

引数のプリロードを可能にする

使い方

モデル.preload(引数..)

User.preload(:posts)
# SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)

ソースコード

左外部結合を使ってすべてのレコードを取得

説明

左外部結合を使ってすべてのレコードを取得

使い方

モデル.eager_load(属性..)

User.eager_load(:posts)
# SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, ...
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
# "users"."id"

ソースコード

取得した値をグループ化

説明

取得した値をグループ化

使い方

モデル.group(グループ化キー..)

usersテーブルをnameでグルーピング

User.group("name")
# SELECT * FROM users GROUP BY name

複数指定

User.group('name, age')
# SELECT * FROM users GROUP BY name, age

年齢ごとの件数

User.group(:age).count

結合してグルーピング

User.joins(:categories).group("categories.name")

3つのテーブル

User.joins(categories: :tags).group("user.name")

ソースコード

取得した値を絞り込む

説明

モデルから取得した値を元に絞り込む

使い方

モデル.having(条件式)

絞り込む

User.having(['AVG(age) >= ?', 30])
# SELECT * FROM users HAVING AVG(age) >= 30

グループ後に絞り込む

User.select('age, count(*) as count').group('age').having('count >= ?', 30)

ソースコード

適応した条件式を除外

説明

モデルに適応した条件式を除外

使い方

モデル.except(条件式..)

Page.order("category DESC").except(:order)

ソースコード

レコードが参照されるテーブルを指定

説明

レコードが参照されるテーブルを指定

使い方

モデル.from(値, サブクエリ=nil)

レコードが参照されるテーブルを指定

Topic.select('title').from('posts')
# SELECT title FROM posts

関連付けモデル

Topic.select('title').from(Topic.approved)
# SELECT title FROM (SELECT * FROM topics WHERE approved = 't') subquery

サブクエリ

Topic.select('a.title').from(Topic.approved, :a)
# SELECT a.title FROM (SELECT * FROM topics WHERE approved = 't') a

ソースコード

SELECT文で使用するヒントを指定

説明

SELECT文で使用するヒントを指定

使い方

モデル.optimizer_hints(引数..)

MySQL

Topic.optimizer_hints("MAX_EXECUTION_TIME(50000)", "NO_INDEX_MERGE(topics)")
# SELECT /*+ MAX_EXECUTION_TIME(50000) NO_INDEX_MERGE(topics) */ `topics`.* FROM `topics`

PostgreSQL

Topic.optimizer_hints("SeqScan(topics)", "Parallel(topics 8)")
# SELECT /*+ SeqScan(topics) Parallel(topics 8) */ "topics".* FROM "topics"

ソースコード

事前に設定したセレクト文を変更

説明

事前に設定したセレクト文を変更

使い方

モデル.reselect(引数..)

Post.select(:title, :body)
# SELECT `posts`.`title`, `posts`.`body` FROM `posts`

Post.select(:title, :body).reselect(:created_at)
# SELECT `posts`.`created_at` FROM `posts`

ソースコード

返されたリレーションをstrict_loadingモードに設定

説明

返されたリレーションをstrict_loadingモードに設定

使い方

モデル.strict_loading(値=true)

# 失敗したら例外発生
モデル.strict_loading!(値=true)

user = User.strict_loading.first
user.comments.to_a
=> ActiveRecord::StrictLoadingViolationError

ソースコード

他の条件とマージ

説明

他の条件とマージ

使い方

モデル.merge(他の条件, *rest)

Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
# Performs a single join query with both where conditions.

recent_posts = Post.order('created_at DESC').first(5)
Post.where(published: true).merge(recent_posts)
# Returns the intersection of all published posts with the 5 most recently created posts.
# (This is just an example. You'd probably want to do this with a single query!)

ソースコード

モデルのコレクションのレコード数を取得

説明

モデルのレコード数を取得

使い方

モデルのコレクション.count(カラム名=nil)

テーブルの行数を計算

User.count

カラム指定

Person.count(:age)

groupで集計してから件数取得

Person.group(:city).count

ソースコード

SQL文を使って件数を取得

説明

SQL文を使って件数を取得

使い方

モデル.count_by_sql(SQL文)

Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
# 12

ソースコード

主キーを全て取得

説明

主キーを全て取得

使い方

モデル.ids()

主キーを全て取得

Person.ids
# SELECT people.id FROM people

JOIN

Person.joins(:companies).ids
# SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id

ソースコード

カラム情報の取得

説明

指定したカラム名のオブジェクトを取得

使い方

モデル.column_for_attribute([カラム名])

@page = Page.find(1)
@page.column_for_attribute(:title)

ソースコード

すべての属性名を配列で取得

説明

すべての属性名を配列で取得

使い方

モデル.attribute_names()

Person.attribute_names
# ["id", "created_at", "updated_at", "name", "age"]

ソースコード

属性の値に似た文字列を取得

説明

属性の値に似た文字列を取得
50文字以降は省略
日時などは:db形式で返却

使い方

モデル.attribute_for_inspect(属性名)

属性の値に似た文字列を取得

person.attribute_for_inspect(:name)
# "\"David Heinemeier Hansson David Heinemeier Hansson D...\""

日時を取得

person.attribute_for_inspect(:created_at)
#=> "\"2012-10-22 00:15:07\""

配列を取得

person.attribute_for_inspect(:tag_ids)
#=> "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"

ソースコード

全てのモデルオブジェクトと属性を取得

説明

全てのモデルオブジェクトと属性を取得

使い方

モデル.attributes()

person = Person.create(name: 'Francesco', age: 22)
person.attributes
# {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}

ソースコード

型に変更前のハッシュを取得

説明

型に変更前のハッシュを取得

使い方

モデル.attributes_before_type_cast()

task = Task.new(title: nil, is_done: true, completed_on: '2012-10-21')
task.attributes
# {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>Sun, 21 Oct 2012, "created_at"=>nil, "updated_at"=>nil}
task.attributes_before_type_cast
# {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}

ソースコード

指定された属性を型に変更前のハッシュを取得

説明

型に変更前のハッシュを取得

使い方

モデル.read_attribute_before_type_cast(カラム名)

task = Task.new(id: '1', completed_on: '2012-10-21')
task.read_attribute('id')                            # 1
task.read_attribute_before_type_cast('id')           # '1'
task.read_attribute('completed_on')                  # Sun, 21 Oct 2012
task.read_attribute_before_type_cast('completed_on') # "2012-10-21"

ソースコード

属性変更を取得

説明

変更点を取得

使い方

モデル.changed()

person.changed # []
person.name = 'bob'
person.changed # ["name"]

ソースコード

指定したカラムのレコードの配列を取得

説明

指定したカラムのレコードの配列を取得

使い方

モデル.pluck(カラム名..)

指定したカラムのレコードの配列を取得

Person.pluck(:id)
# SELECT people.id FROM people

複数指定

Person.pluck(:id, :name)
# SELECT people.id, people.name FROM people

whereなどと一緒に使用

Person.where(age: 21).limit(5).pluck(:id)
# SELECT people.id FROM people WHERE people.age = 21 LIMIT 5

ソースコード

取得済みのモデルから先頭のレコードの値を1件取得

説明

whereやorderなどで取得済みのモデルから先頭のレコードの値を1件取得
relation.limit(1).pluck().firstの省略形

使い方

pick(カラム名..)

User.where(id: 1).pick(:name)

ソースコード

モデルの変更前と変更後の値をハッシュで取得

説明

モデルの変更前と変更後の値をハッシュで取得

使い方

モデル.changes()

person.changes # {}
person.name = 'bob'
person.changes # {name: ["bill", "bob"]}

ソースコード

指定されたテーブルのインデックスを配列で取得

説明

指定されたテーブルのインデックスを配列で取得

使い方

indexes(テーブル名)

indexes(:pages)

ソースコード

このクエリを識別できるたキャッシュキーを取得

説明

このクエリを識別できるたキャッシュキーを取得

使い方

モデル.cache_key(タイムスタンプカラム='updated_at')

キャッシュキーを取得

Product.where("name like ?", "%Cosmic Encounter%").cache_key

タイムスタンプカラムを指定

Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)

ソースコード

モデルが保存される前に変更された値をハッシュで取得

説明

保存される前に変更された値をハッシュで取得

使い方

モデル.previous_changes()

person.name # "bob"
person.name = 'robert'
person.save
person.previous_changes # {name: ["bob", "robert"]}

ソースコード

モデルから読み込まれたすべてのデータベース・フィールドの名前を取得

説明

モデルから読み込まれたすべてのデータベース・フィールドの名前を取得
開発するときにどのフィールドを選択する必要があるかを判断するのに役立つ

使い方

モデル.accessed_fields()

class PostsController < ActionController::Base
    after_action :print_accessed_fields, only: :index

    def index
        @posts = Post.all
    end

    private

    def print_accessed_fields
        p @posts.first.accessed_fields
    end
end

ソースコード

モデルを再取得

説明

レコードを再取得

使い方

モデル.reload(オプション=nil)

@page = Page.find(1)
# SELECT "pages".* FROM "pages" WHERE "pages"."id" = ? LIMIT 1  [["id", 1]]
@page.reload
# SELECT "pages".* FROM "pages" WHERE "pages"."id" = ? LIMIT 1  [["id", 1]]

ソースコード

データベースに保存

説明

生成したモデルオブジェクトをデータベースに保存

使い方

モデル.save(オプション引数)

# 失敗したら例外発生
モデル.save!(オプション引数)

生成したモデルオブジェクトをデータベースに保存

user = User.new
user.name = "A"
user.save

新しいローカル変数を作らずに保存

User.new do |i|
  i.name = "A"
  i.save
end

属性ハッシュの保存

user = User.new(name: "A")
user.save

データベースへの保存の有無で処理を分ける

if @user.save
  redirect_to :list
else
  render action: "new"
end

複数の項目を保存

begin
  Entry.transaction do
    @many_entries.each { |entry| entry.save! }
  end
 vredirect_to :list
  rescue ActionRecord::RecordInvalid, ActionRecord::RecordNotSaved: ex
  render action: "input_multi_entries"
end

属性を変更して保存

@entry = Entry.find(params[:id])
@entry.message = "new Message"
@entry.save

複数の項目を1度に更新

@user = User.find(parms[:id])
@user.attributes = { username: 'Phusion', is_admin: true }
@user.save

ソースコード

データベースを更新

説明

条件に一致するレコードを更新

使い方

モデル.update(ID=:all, 属性)

# 失敗したら例外発生
モデル.update!(ID=:all, 属性)

条件に一致する属性を更新

Person.update(15, user_name: "Samuel", group: "expert")

ハッシュを使って更新

people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
Person.update(people.keys, people.values)

更新メソッドの種類

メソッド バリデーションの有無
update
update_all ×
update_attributes
update_attribute ×

ソースコード

条件に一致するレコードをすべて更新

説明

条件に一致するレコードをSQLを直接実行して全て更新
updated_atとupdated_onは更新されない

使い方

モデル.update_all(SQL文のSET部分、配列、またはハッシュ)

オプション

オプション 説明
:limit 取得する件数
:order 並び順

与えられた属性を持つすべてのデータを更新

Customer.update_all wants_email: true

条件を絞り込んで更新

Book.where('title LIKE ?', '%Rails%').update_all(author: 'David')

更新するデータ数を固定

Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(author: 'David')

全てのデータを更新して指定したカラムに値を設定

Invoice.update_all('number = id')

更新メソッドの種類

メソッド バリデーションによるバリデーションの有無
update
update_all ×
update_attribute ×

ソースコード

属性ハッシュを指定して更新

説明

条件に一致するモデルオブジェクトを更新
バリデーションはスキップ

使い方

モデル.update_attribute(属性名, 値)

条件に一致するレコードを更新

@user.update_attribute(:name, "hoge")

更新メソッドの種類

メソッド バリデーションによるバリデーションの有無
update
update_all ×
update_attribute ×

ソースコード

update_atを更新

説明

update_atを現在の時刻でアップデート
バリデーションやコールバックを実施されない

使い方

モデル.touch([名前])

update_atを現在の時刻でアップデート

product.touch

update_atを指定した時刻でアップデート

product.touch(time: Time.new(2015, 2, 16, 0, 0, 0))
# updates updated_at/on with specified time

ソースコード

updated_atとupdated_on属性を現在の時刻または指定された時刻に更新

説明

updated_atとupdated_on属性を現在の時刻または指定された時刻に設定

使い方

モデル.touch_all(時刻のカラム名.., time: 時間=nil)

現在時刻に設定

Person.all.touch_all
# "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670'"

時刻のカラム名を指定

Person.all.touch_all(:created_at)
# "UPDATE \"people\" SET \"updated_at\" = '2018-01-04 22:55:23.132670', \"created_at\" = '2018-01-04 22:55:23.132670'"

時間を指定

Person.all.touch_all(time: Time.new(2020, 5, 16, 0, 0, 0))
# "UPDATE \"people\" SET \"updated_at\" = '2020-05-16 00:00:00'"

ソースコード

現在のリレーションのレコードのカウンタを更新

説明

現在のリレーションのレコードのカウンタを更新

使い方

モデル.update_counters(ID, 条件)

Post.where(author_id: author.id).update_counters(comment_count: 1)

ソースコード

SQL文を発行してデータベースの属性を直接更新

説明

SQL文を発行してデータベースの属性を直接更新

使い方

モデル.update_columns(属性)

user.update_columns(last_request_at: Time.current)

ソースコード

複数レコードを一括登録

説明

複数レコードを一括登録
直接SQLを実行するのでバリデーションやコールバックはスキップ

使い方

モデル.insert_all(属性, returning: nil, unique_by: nil, record_timestamps: nil)

# 失敗したら例外発生
モデル.insert_all!(属性, returning: nil, unique_by: nil, record_timestamps: nil)

オプション

オプション 説明
   
:returning 戻り値の属性を指定(PostgreSQLのみ)
:unique_by 重複でスキップするカラムを指定(PostgreSQLとSQLiteのみ)
:record_timestamps タイムスタンプの自動設定を強制

Book.insert_all([
  { id: 1, title: "Rework", author: "David" },
  { id: 1, title: "Eloquent Ruby", author: "Russ" }
])

ソースコード

指定した条件に一致するレコードをSQLを直接実行して削除

説明

指定した条件に一致するレコードをSQLを直接実行して削除
関連付けられたモデルは削除しない

使い方

モデル.delete(IDかIDの配列)

指定した条件に一致するレコードをSQLを直接実行して削除

Todo.delete(1)

配列で指定

Todo.delete([2,3,4])

ソースコード

指定した条件に一致するレコードをSQLを直接実行して全て削除

説明

指定した条件に一致するレコードをSQLを直接実行して全て削除
関連付けられたモデルは削除しない

使い方

モデル.delete_all()

Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all

ソースコード

指定された条件に一致するすべてのレコードを検索して削除

説明

指定された条件に一致するすべてのレコードを検索して削除

使い方

モデル.delete_by(条件..)

Person.delete_by(id: 13)
Person.delete_by(name: 'Spartacus', rating: 4)
Person.delete_by("published_at < ?", 2.weeks.ago)

ソースコード

指定した条件のレコードを削除

説明

指定した条件のレコードを削除
dependentが設定されている場合は関連付けられたモデルも削除

使い方

モデル.destroy(ID)

指定した条件のレコードを削除

Todo.destroy(1)

複数指定

Todo.destroy([1,2,3])

ソースコード

指定した条件のレコードと関連しているレコードをすべて削除

説明

指定した条件のレコードと関連しているレコードをすべて削除
dependentが設定されている場合は関連付けられたモデルも削除

使い方

モデル.destroy_all()

Person.where(age: 0..18).destroy_all

ソースコード

指定された条件に一致するレコードと関連するレコードを検索してすべて削除

説明

指定された条件に一致するレコードと関連するレコードを検索してすべて削除

使い方

モデル.destroy_by(条件..)

Person.destroy_by(id: 13)
Person.destroy_by(name: 'Spartacus', rating: 4)
Person.destroy_by("published_at < ?", 2.weeks.ago)

ソースコード

コピーを生成

説明

モデルのコピーを生成

使い方

モデル.clone()

user = User.first
new_user = user.clone
user.name               # "Bob"
new_user.name = "Joe"
user.name               # "Joe"

ソースコード

特定のレコードをインクリメント

説明

特定のレコードをインクリメント

使い方

モデル.increment(レコード, インクリメントする値, touch: タイムスタンプ列=nil)

# 失敗したら例外発生
モデル.increment!(レコード, インクリメントする値, touch: タイムスタンプ列=nil)

Page.increment(:num, 3)

ソースコード

保存されていない変更が加えられた属性の元の値を示すハッシュ

説明

保存されていない変更が加えられた属性の元の値を示すハッシュ

使い方

モデル.changed_attributes()

person.name #=> "bob"
person.name = 'robert'
person.changed_attributes #=> {"name" => "bob"}

ソースコード

指定した属性が存在するかチェック

説明

指定した属性が存在するかチェック

使い方

モデル.attribute_present?(属性名)

person.attribute_present?(:title)
# true

ソースコード

モデルが指定したメソッドを呼び出せるかチェック

説明

モデルが指定したメソッドを呼び出せるかチェック

使い方

モデル.respond_to?(メソッド名, include_private=false)

Personモデルがname属性を呼び出せるかチェック

person.respond_to?(:name)
# true

プライベートメソッドを呼び出せるかチェック

person.respond_to?(:private_name, true)
# true

ソースコード

モデルに指定した属性が存在するかチェック

説明

モデルに指定した属性が存在するかチェック

使い方

モデル.has_attribute?(属性)

指定した属性が存在する場合

person.has_attribute?(:name)
# true

指定した属性が存在しない場合

person.has_attribute?(:nothing)
# false

ソースコード

与えられた関係がこの関係と構造的に互換性があるかどうかをチェック

説明

与えられた関係がこの関係と構造的に互換性があるかどうかをチェックし、エラーを出さずにandやorのメソッドを使用できるかどうかを判断

使い方

モデル.structurally_compatible?(調べる条件)

Post.where("id = 1").structurally_compatible?(Post.where("author_id = 3"))
#=> true

Post.joins(:comments).structurally_compatible?(Post.where("id = 1"))
#=> false

ソースコード

保存していないレコードがあるかチェック

説明

保存していないレコードがあるかチェック

使い方

モデル.changed?()

@user.changed?

ソースコード

指定したモデルオブジェクトの属性が存在するかチェック

説明

指定したモデルの属性が存在するかチェック

使い方

モデル.attribute_method?(属性)

true

User.attribute_method?(:name)
# true

false

User.attribute_method?(:age)
# false

ソースコード

レコードの存在をチェック

説明

指定したレコードが存在するか

使い方

モデル.exists?([条件])

Pagesテーブルに1件でもデータは存在するか確認

Page.exists?
# SELECT 1 FROM "pages" LIMIT 1

categoryがrailsであるデータが存在するか確認

Page.exists?(category: "rails")
# SELECT 1 FROM "pages" WHERE "pages"."category" = "rails" LIMIT 1

ソースコード

新しいレコードかチェック

説明

新しいレコードかチェック

使い方

モデル.new_record?()

User.new_record?

ソースコード

保存済みかチェック

説明

保存済みかチェック

使い方

モデル.persisted?()

User.persisted?

ソースコード

削除済みかチェック

説明

削除済みかチェック

使い方

モデル.destroyed?()

@user.destroyed?

ソースコード

テーブルに外部キー制約が存在するかチェック

説明

テーブルに外部キー制約が存在するか

使い方

foreign_key_exists?(from_table [, to_table = nil, オプション])

テーブルに外部キー制約が存在するか

foreign_key_exists?(:accounts, :branches)

カラムを指定

foreign_key_exists?(:accounts, column: :owner_id)

nameを指定

foreign_key_exists?(:accounts, name: "special_fk_name")

ソースコード

例外が発生するかチェック

説明

例外が発生するかチェック

使い方

モデル.instance_method_already_implemented?(属性)

例外が発生する

Person.instance_method_already_implemented?(:save)
# ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord

例外が発生しない

Person.instance_method_already_implemented?(:name)
# false

ソースコード

オブジェクトがモデルを実装するように設計されているかチェック

説明

オブジェクトがモデルを実装するように設計されているか

使い方

to_model()

class Person
    include ActiveModel::Conversion
end
person = Person.new
person.to_model == person
# true

ソースコード

複数カラムをまとめる

説明

複数カラムを擬似的に1つにまとめる

使い方

モデル.composed_of(条件, オプション={})

オプション

オプション 説明
:class_name クラス名
:mapping マッピング
:allow_nil nilのバリデーションをスキップ
:constructor 条件
:converter 新しい値が値オブジェクトに割り当てられたときに呼び出される

複数カラムを擬似的に1つにまとめる

composed_of :temperature, mapping: %w(reading celsius)

マッピング指定

composed_of :balance, class_name: "Money", mapping: %w(balance amount), converter: Proc.new { |balance| balance.to_money }

ソースコード

URLのidの部分にid以外のものを指定

説明

URLのidの部分にid以外のものを指定

使い方

to_param()

class User < ActiveRecord::Base
  def to_param  # overridden
    name
  end
end

ソースコード

属性に反対のブール値を割り当てる

説明

属性に反対のブール値を割り当てる

使い方

モデル.toggle(属性)

# 失敗したら例外発生
モデル.toggle!(属性)

user = User.first
user.banned? #=> false
user.toggle(:banned)
user.banned? #=> true

ソースコード

関連モデルオブジェクトから新しい属性を作成

説明

関連モデルオブジェクトから新しい属性を作成
「nil」を引数で渡すと属性をリセット

使い方

モデル.create_with(属性)

関連オブジェクトから新しいモデルを作成

users = users.create_with(name: 'DHH')
users.new.name
# 'DHH'

属性をリセット

users = users.create_with(nil)
users.new.name
# 'Oscar'

ソースコード

生成されるクエリにSQLコメントを追加

説明

生成されるクエリにSQLコメントを追加

使い方

モデル.annotate(引数..)

User.annotate("selecting user names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting user names */

User.annotate("selecting", "user", "names").select(:name)
# SELECT "users"."name" FROM "users" /* selecting */ /* user */ /* names */

ソースコード

指定したレコードを結果のリレーションから除外

説明

指定したレコードを結果のリレーションから除外

使い方

モデル.excluding(レコード..)

Post.excluding(post)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" != 1

Post.excluding(post_one, post_two)
# SELECT "posts".* FROM "posts" WHERE "posts"."id" NOT IN (1, 2)

ソースコード

モデルをハッシュ形式のJSONに変換

説明

モデルをハッシュ形式のJSONに変換

使い方

モデル.as_json(オプション=nil)

オプション

オプション 説明 デフォルト値
:root ノード名を含める false
:only 取得する要素を指定  
:except 取得しない要素を指定  
:methods メソッドの呼び出し結果を含める  
:include 関連付け  

ハッシュ形式のJSONに変換

user.as_json
# {id: 1, name: "Konata Izumi", age: 16, created_at: "2006-08-01T17:27:133.000Z", awesome: true}

ノード名を含める

user.as_json(root: true)
#=> { "user" => { "id" => 1, "age" => 16, "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true } }

取得する要素を指定

user.as_json(only: [:id, :age])
#=> { "id" => 1, "age" => 16 }

取得しない要素を指定

user.as_json(except: [:id, :created_at, :name])
#=> { "age" => 16, "awesome" => true }

メソッドを指定

user.as_json(methods: :permalink)
#=> { "id" => 1, "name" => "Konata Izumi", "age" => 16, "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, "permalink" => "1-konata-izumi" }

関連付け

user.as_json(include: :posts)
#=> { "id" => 1, "name" => "Konata Izumi", "age" => 16, "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true, "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" }, { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }

ソースコード

JSON文字列からモデルの属性を設定

説明

JSON文字列からモデルの属性を設定

使い方

モデル.from_json(JSON文字列, include_root=include_root_in_json)

json = { name: 'bob', age: 22, awesome:true }.to_json
person = Person.new
person.from_json(json) #=> #<Person:0x007fec5e7a0088 @age=22, @awesome=true, @name="bob">
person.name            #=> "bob"
person.age             #=> 22
person.awesome         #=> true

ソースコード

全てのバリデーションをクリア

説明

全てのバリデーションをクリア

使い方

モデル.clear_validators!()

Person.clear_validators!

ソースコード

分割してレコードを取得して処理

説明

分割してレコードを取得して処理
デフォルトで1000件ずつ処理

使い方

モデル.in_batches([オプション]) do |i|
  処理内容
end

オプション

オプション 説明 デフォルト値
:of 同時処理数 1000
:load ロードするか false
:start 処理開始位置  
:finish 終了するときのキー  
:error_on_ignore 例外を発生させる  
:order 主キーの順序を指定 :asc

分割してレコードを取得して処理

Person.where("age > 21").in_batches do |relation|
  relation.delete_all
  sleep(10) # Throttle the delete queries
end

分割して取得して削除

Person.in_batches.delete_all

分割して取得して更新

Person.in_batches.update_all(awesome: true)

補足

  • find_in_batchesとの違いはActiveRecord::Relationで値を返す

ソースコード

指定した条件以外の条件をクエリから除外

説明

指定した条件以外の条件をクエリから削除

使い方

モデル.only(条件..)

条件式を削除

Post.order('id asc').only(:where)

複数指定

Post.order('id asc').only(:where, :order)

ソースコード

トランザクション

説明

分割不可能な複数のレコードの更新を1つの単位にまとめて処理すること

特徴

  • データベース内の情報の整合性を保つための手段
  • 複数のデータベースにまたがる分散トランザクションはサポートしていない
  • 使用するにはデータベースがトランザクションをサポートしていることが必要

使い方

モデル.transaction(requires_new: 新規作成=nil, isolation: isolation=nil, joinable: 結合=true, ブロック引数)
  • ブロック内のすべての処理が正常に行われた場合に保存
  • エラーが発生した場合は、ロードバック

def new
  User.transaction do
    a1 = User.new(name: 'tarou')
    a1.save!
    a2 = User.new(name: 'jirou')
    a2.save!
  end
  render text: '更新に成功しました'
  rescue: e
  render text: e.message
end

ソースコード

データベースのロック

楽観的ロック

あらかじめカラムのロックバージョンを記録しておき、更新時にロックバージョンが変わっていないことをバリデーションして保存

class CreateAddLockColumnToEntries < ActiveRecord::Migration
  def self.up
    create_table :add_lock_column_to_entries do |t|
      t.integer :entries, :lock_version, null: false, default: 0
    end
  end

  def self.down
    drop_table :add_lock_column_to_entries
  end
end

悲観的ロック

データベース側でレコードをロックし、並行に更新できないようにする

locked_entry = Entry.find(1,lock: true)
  • MySQLとPostgreSQLでのみ利用可能

特定の値のセットによる順序の指定

説明

特定の値のセットによる順序の指定

使い方

モデル.in_order_of(カラム, 値)

User.in_order_of(:id, [1, 5, 3])
# SELECT "users".* FROM "users" ORDER BY FIELD("users"."id", 1, 5, 3)

ソースコード

属性のエイリアスを作成

説明

属性のエイリアスを作成

使い方

alias_attribute(エイリアス名, エイリアス元の属性名)

alias_attribute :subject, :title

ソースコード

属性のハッシュを渡すことですべての属性を設定

説明

属性のハッシュを渡すことですべての属性を設定

使い方

assign_attributes(属性のハッシュ)

class Cat
  include ActiveModel::AttributeAssignment
  attr_accessor :name, :status
end
cat = Cat.new
cat.assign_attributes(name: "Gorby", status: "yawning")
cat.name #=> 'Gorby'
cat.status #=> 'yawning'
cat.assign_attributes(status: "sleeping")
cat.name #=> 'Gorby'
cat.status #=> 'sleeping'

ソースコード

すべての属性名を配列で取得

説明

すべての属性名を配列で取得

使い方

to_key()

class Person
    include ActiveModel::Conversion
    attr_accessor :id
    def initialize(id)
        @id = id
    end
end
person = Person.new(1)
person.to_key #=> [1]

ソースコード

URLでの使用に適したオブジェクトのキーを表す文字列

説明

URLでの使用に適したオブジェクトのキーを表す文字列

使い方

to_param()

class Person
    include ActiveModel::Conversion
    attr_accessor :id
    def initialize(id)
        @id = id
    end
    def persisted?
        true
    end
end
person = Person.new(1)
person.to_param
#=> "1"

ソースコード

オブジェクトに関連するパスを示す文字列

説明

オブジェクトに関連するパスを示す文字列

使い方

to_partial_path()

class Person
    include ActiveModel::Conversion
end
person = Person.new
person.to_partial_path
#=> "people/person"

ソースコード

BCryptパスワードを設定して認証するためのメソッド

説明

BCryptパスワードを設定して認証するためのメソッド

使い方

has_secure_password(属性=:password, validations: バリデーション=true)

class User < ActiveRecord::Base
    has_secure_password
    has_secure_password :recovery_password, validations: false
end
user = User.new(name: 'david', password: '', password_confirmation: 'nomatch')
user.save                                                  #=> false, password required
user.password = 'mUc3m00RsqyRe'
user.save                                                  #=> false, confirmation doesn't match
user.password_confirmation = 'mUc3m00RsqyRe'
user.save                                                  #=> true
user.recovery_password = "42password"
user.recovery_password_digest                              #=> "$2a$04$iOfhwahFymCs5weB3BNH/uXkTG65HR.qpW.bNhEjFP3ftli3o5DQC"
user.save                                                  #=> true
user.authenticate('notright')                              #=> false
user.authenticate('mUc3m00RsqyRe')                         #=> user
user.authenticate_recovery_password('42password')          #=> user
User.find_by(name: 'david')&.authenticate('notright')      #=> false
User.find_by(name: 'david')&.authenticate('mUc3m00RsqyRe') #=> user

ソースコード

オブジェクトのシリアル化されたハッシュ

説明

オブジェクトのシリアル化されたハッシュ

使い方

serializable_hash(オプション=nil)

色々な使用方法

class Person
    include ActiveModel::Serialization
    attr_accessor :name, :age
    def attributes
        {'name' => nil, 'age' => nil}
    end
    def capitalized_name
        name.capitalize
    end
end
person = Person.new
person.name = 'bob'
person.age  = 22
person.serializable_hash                #=> {"name"=>"bob", "age"=>22}
person.serializable_hash(only: :name)   #=> {"name"=>"bob"}
person.serializable_hash(except: :name) #=> {"age"=>22}
person.serializable_hash(methods: :capitalized_name)
#=> {"name"=>"bob", "age"=>22, "capitalized_name"=>"Bob"}

ソースコード

リレーションから名前付きのアソシエーションを抽出

説明

リレーションから名前付きのアソシエーションを抽出

使い方

extract_associated(アソシエーション)

account.memberships.extract_associated(:user)
#=> Returns collection of User records

ソースコード

レコードがまだ読み込まれていない場合にデータベースからレコードを読み込む

説明

レコードがまだ読み込まれていない場合にデータベースからレコードを読み込む

使い方

モデル.load([ブロック])

Post.where(published: true).load
#=> #<ActiveRecord::Relation>

ソースコード

バックグラウンドのスレッドプールからクエリを実行するようにスケジュール

説明

バックグラウンドのスレッドプールからクエリを実行するようにスケジュール

使い方

モデル.load_async()

Post.where(published: true).load_async
#=> #<ActiveRecord::Relation>

ソースコード

デフォルトのスコープを定義

説明

デフォルトのスコープを定義

使い方

default_scope(条件式=nil, all_queries: 全てのクエリ=nil, ブロック引数)

class Article < ActiveRecord::Base
  default_scope { where(published: true) }
end
Article.all
# SELECT * FROM articles WHERE published = true

ソースコード

すでに設定されているスコープを除いたモデルのスコープを取得

説明

すでに設定されているスコープを除いたモデルのスコープを取得

使い方

unscoped(ブロック引数)

でに設定されているスコープを除いたモデルのスコープを取得

Post.unscoped.all

ブロック

Post.unscoped {
  Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
}

ソースコード

条件式を削除

説明

条件式を削除

使い方

モデル.unscope(条件式..)

条件式を削除

User.order('email DESC').unscope(:order)

複数指定

User.select(:id).order('email DESC').unscope(:select, :order)

ソースコード

scopeを使ってモデルを拡張

説明

モジュールやブロックメソッドを引数に与えてモデルをscopeで拡張
返されたオブジェクトをさらに拡張することもできる

使い方

モデル.extending(モジュール.., ブロック引数)

モジュール

module Pagination
  def page(number)
    # pagination code goes here
  end
end

scope = Model.all.extending(Pagination)
scope.page(params[:page])

ブロック

module Pagination
  def page(number)
    # pagination code goes here
  end
end

scope = Model.all.extending do
  def page(number)
    # pagination code goes here
  end
end
scope.page(params[:page])

ソースコード

すべてのクエリを現在のスコープに収める

説明

すべてのクエリを現在のスコープに収める

使い方

モデル.scoping(all_queries: 全てのクエリ=nil, ブロック引数)

Comment.where(post_id: 1).scoping do
    Comment.first
end
#=> SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1 ORDER BY "comments"."id" ASC LIMIT 1

ソースコード

取得する時のSQLを表示

説明

取得する時のSQLを表示

使い方

モデル.to_sql()

User.where(name: 'Oscar').to_sql
# SELECT "users".* FROM "users"  WHERE "users"."name" = 'Oscar'

ソースコード

親と子のテーブルのレコードの継承関係

説明

親と子のテーブルのレコードの継承関係

使い方

delegated_type(ロール, types: 型=nil, オプション引数)

オプション

オプション 説明 デフォルト値
     
:foreign_key 関連オブジェクトで使用する外部キー名 :foreign_key
:primary_key 関連オブジェクトの主キー id

class Entry < ApplicationRecord
    delegated_type :entryable, types: %w[ Message Comment ], dependent: :destroy
end

Entry#entryable_class #=> +Message+ or +Comment+
Entry#entryable_name  #=> "message" or "comment"
Entry.messages        #=> Entry.where(entryable_type: "Message")
Entry#message?        #=> true when entryable_type == "Message"
Entry#message         #=> returns the message record, when entryable_type == "Message", otherwise nil
Entry#message_id      #=> returns entryable_id, when entryable_type == "Message", otherwise nil
Entry.comments        #=> Entry.where(entryable_type: "Comment")
Entry#comment?        #=> true when entryable_type == "Comment"
Entry#comment         #=> returns the comment record, when entryable_type == "Comment", otherwise nil
Entry#comment_id      #=> returns entryable_id, when entryable_type == "Comment", otherwise nil

ソースコード

カラムの平均値を計算

説明

カラムの平均値を計算

使い方

モデル.average(カラム名)

Page.average(:count)
# SELECT AVG("pages"."count") AS avg_id FROM "pages"

ソースコード

カラムの最大値を計算

説明

カラムの最大値を計算

使い方

モデル.maximum(カラム名)

User.maximum('age')
# SELECT MAX("users"."age") AS max_id FROM "users"

ソースコード

カラムの最小値を計算

説明

カラムの最小値を計算

使い方

モデル.minimum(カラム名)

User.minimum('age')
# SELECT MIN("users"."age") AS min_id FROM "users"

ソースコード

カラムの合計値を計算

説明

カラムの合計値を計算

使い方

モデル.sum(カラム名=nil, ブロック引数)

Rating.sum(:score)
#  SELECT SUM("Ratings"."score") AS sum_id FROM "Ratings"

ソースコード

汎用的なカラム処理

説明

汎用的なカラム処理

使い方

モデル.calculate(処理方法, カラム名)

処理方法

処理方法 説明
:count カウント
:sum 合計値
:average 平均値
:minimum 最小値
:maximum 最大値

Page.calculate(:count, :all)
# SELECT COUNT(*) FROM "pages"

ソースコード