モデルについて
layout: page
説明
モデルとはアプリケーションが扱うデータや処理を表現する仕組みのこと
モデル名とテーブル名の規約
- 英大文字から始まる
- 英数字のみ
- 単語の区切りでは、先頭文字を大文字
- 単数形の名詞
- Entry
- UserComment
- ファイルはapp/modelsディレクトリに格納
- ファイル名は、モデル名の単語区切りを「_」にし、すべて小文字にしたもの
- app/models/entry.rb
- app/models/user_comment.rb
命名規則
種類 | 説明 | 例 |
---|---|---|
モデル名 | 先頭は大文字で単数形 | User |
モデルのファイル名 | 先頭は小文字で単数形 | user.rb |
テーブル名 | 先頭は小文字で複数形 | users |
テストスクリプト名 | xxx_test.rb | user_test.rb |
モデルを生成
layout: page
説明
モデルオブジェクトを生成
保存はまだされていないため、saveメソッドなどを使って保存
生成と同時に保存したい場合は、createメソッドを使用
使い方
モデル.new([属性])
例
モデルオブジェクトを生成
User.new
# <User id: nil, created_at: nil, updated_at: nil>
属性を指定
User.new(name: "DHH")
# <User id: nil, name: "DHH", created_at: nil, updated_at: nil>
ブロックで指定
users.new do |user|
user.name = 'Oscar'
end
ソースコード
モデルを生成(newの別名)
layout: page
説明
モデルオブジェクトを生成
newの別名
使い方
モデル.build([属性])
例
モデルを生成
person.pets.build
# <Pet id: nil, name: nil, person_id: 1>
属性を指定
person.pets.build(name: 'Fancy-Fancy')
# <Pet id: nil, name: "Fancy-Fancy", person_id: 1>
ブロックを指定
person.pets.build([{name: 'Spook'}, {name: 'Choo-Choo'}, {name: 'Brain'}])
# [
# <Pet id: nil, name: "Spook", person_id: 1>,
# <Pet id: nil, name: "Choo-Choo", person_id: 1>,
# <Pet id: nil, name: "Brain", person_id: 1>
# ]
ソースコード
モデルを生成して保存
layout: page
説明
モデルオブジェクトを生成して保存
生成のみしたい場合は、newメソッドを使用
使い方
モデル.create([属性])
失敗したら例外
モデル.create!([属性])
例
モデルを生成して保存
User.create(name: "TestUser", profile: "profile")
属性ハッシュの配列を保存
User.create([{name: "A"},{name: "B"}])
ソースコード
条件を指定して最初の1件を取得
layout: page
説明
条件を指定して最初の1件を取得
存在しない場合はnil
使い方
モデル.find_by(条件)
取得するレコードが存在しない場合に例外が発生
モデル.find_by!(条件)
例
category_idが1の最初の値を取得
Page.find_by category_id: 1
# <Page id: 1, 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
ソースコード
IDを指定してレコードを取得
layout: page
説明
IDを指定してレコードを取得
指定したIDの中に存在しないIDが1つでもあると例外が発生
使い方
モデル.find(件数)
例
IDを指定してレコードを取得
Person.find(1)
# <Person id: 1, ...>
複数IDを指定
Person.find(1, 2, 6)
# [
# <Person id: 1, ...>,
# <Person id: 2, ...>,
# <Person id: 6, ...>
# ]
配列で指定
Person.find([7, 17])
# [
# <Person id: 7, ...>,
# <Person id: 17, ...>
# ]
ソースコード
分割してレコードを取得して1件ずつ処理
layout: page
説明
分割してレコードを取得して1件ずつ処理
デフォルトでは1000件ずつ処理
大きなデータをもつモデルなどを処理する時に使う
使い方
モデル.find_each([オプション]) do |i|
処理内容
end
オプション
オプション | 説明 | デフォルト値 |
---|---|---|
:batch_size | 同時処理数 | 1000 |
:start | 処理開始位置 | |
:finish | 終了するときのキー | |
:error_on_ignore | 例外を発生させる |
例
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件ずつ処理すること
ソースコード
分割してレコードを取得して処理
layout: page
説明
分割してレコードを取得して処理
デフォルトで1000件ずつ処理
使い方
モデル.find_in_batches([オプション]) do |i|
処理内容
end
オプション
オプション | 説明 | デフォルト値 |
---|---|---|
:batch_size | 同時処理数 | 1000 |
:start | 処理開始位置 | |
:finish | 終了するときのキー | |
:error_on_ignore | 例外を発生させる |
例
分割してレコードを取得して処理
Person.where("age > 21").find_in_batches do |group|
sleep(50) # Make sure it doesn't get too crowded in there!
group.each { |person| person.party_all_night! }
end
同時処理数を2000
Person.all.find_in_batches(start: 2000, batch_size: 2000) do |group|
group.each { |person| person.party_all_night! }
end
補足
- 処理する順序は指定できない
- find_eachとの違いは配列で値を受けとること
- 順番に処理したい場合は、find_eachを使用
ソースコード
条件を指定して初めの1件を取得し1件もなければ作成(create)
layout: page
説明
条件を指定して初めの1件を取得し、1件もなければ作成
使い方
モデル.find_or_create_by(条件)
新しいレコードが無効な場合に例外
モデル.find_or_create_by!(条件)
例
存在しないので新しく作成
User.find_or_create_by(first_name: 'Penélope')
# <User id: 1, first_name: 'Penélope', last_name: nil>
既に存在する場合は既存のレコードを取得
User.find_or_create_by(first_name: 'Penélope')
# <User id: 1, first_name: 'Penélope', last_name: nil>
ブロック指定
User.find_or_create_by(first_name: 'Scarlett') do |user|
user.last_name = "O'Hara"
end
# <User id: 2, first_name: 'Scarlett', last_name: 'Johansson'>
補足
- find_or_create_byとの違いは、作成する時に呼ぶメソッドがnewではなくcreate
ソースコード
データベースのユニーク制約を使って作成、できなければ初めの1件を取得
layout: page
説明
データベースのユニーク制約を使って作成、できなければ初めの1件を取得
find_or_create_byでは作成されるまでに別プロセスによって作成されている可能性があったので、その問題を解決した処理
create_or_find_by!はエラーの時に例外が発生
使い方
create_or_find_by(属性)
ブロック指定
create_or_find_by(属性) do
end
例
データベースのユニーク制約を使って作成、できなければ初めの1件を取得
User.create_or_find_by(first_name: 'Penélope')
ソースコード
条件を指定して初めの1件を取得し1件もなければ作成(new)
layout: page
説明
条件を指定して初めの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
ソースコード
SQLを直接指定して取得
layout: page
説明
SQL文を直接書いて取得
使い方
モデル.find_by_sql(SQL文)
例
SQLを直接使って検索
Page.find_by_sql("SELECT * FROM pages LIMIT 10")
# SELECT * FROM pages LIMIT 10
ソースコード
SQLを直接指定して取得(非モデルオブジェクト)
layout: page
説明
SQL文を直接書いて取得
使い方
select_all(SQL文, [名前, binds, preparable])
例
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との違いは取得したオブジェクトのインスタンス化は行わないこと
ソースコード
すべてのレコードを取得
layout: page
説明
すべてのレコードを取得
使い方
モデル.all
例
Pagesテーブルからすべてのレコードを取得
Page.all
# SELECT "pages".* FROM "pages"
ソースコード
先頭のレコードを取得
layout: page
説明
モデルの先頭のレコードを取得
使い方
モデル.first([件数])
取得するレコードが存在しない場合に例外が発生
モデル.first!([件数])
例
モデルの先頭のレコードを取得
Page.first
# <Page id: 1, ...>
# SQL: SELECT "pages".* FROM "pages" LIMIT 1
複数件数を取得
Page.first(3)
# [
# <Page id: 1, ...>,
# <Page id: 2, ...>,
# <Page id: 3, ...>
# ]
# SQL: SELECT "pages".* FROM "pages" LIMIT 3
orderで並び替え後のレコードを取得
Page.order(:title).first
# <Page title: a, ...>
# SQL: SELECT * FROM pages ORDER BY pages.title ASC LIMIT 1
モデルが空の場合
Page.first
# nil
ソースコード
モデルが指定したメソッドを呼び出せるかチェック
layout: page
説明
モデルが指定したメソッドを呼び出せるかチェック
使い方
モデル.respond_to?(メソッド名 [, include_private=false])
例
Personモデルがname属性を呼び出せるかチェック
person.respond_to?(:name)
# true
プライベートメソッドを呼び出せるかチェック
person.respond_to?(:private_name, true)
# true
ソースコード
最後のレコードを取得
layout: page
説明
モデルの最後のレコードを取得
使い方
モデル.last([件数])
取得するレコードが存在しない場合に例外が発生
モデル.last!([件数])
例
pagesテーブルの最後のレコードを取得
Page.last
# #<Page id: 10, ...>
# SQL: SELECT "pages".* FROM "pages" ORDER BY "pages"."id" DESC LIMIT 1
複数を取得
Page.last(2)
# [
# <Page id: 10, ...>,
# <Page id: 9, ...>,
# <Page id: 8, ...>
# ]
# SELECT "pages".* FROM "pages" ORDER BY "pages"."id" DESC LIMIT 3
orderで並び替え後のレコードを取得
Page.order(:title).last
# <Page title: z, ...>
# SQL: SELECT * FROM pages ORDER BY pages.title DESC LIMIT 1
モデルが空の場合
Page.last
# nil
ソースコード
指定した数のレコードを取得
layout: page
説明
引数で指定した件数のレコードを取得
取得するレコードをIDなどで指定することはできない
使い方
モデル.take([件数])
取得するレコードが存在しない場合に例外が発生
モデル.take!([件数])
例
1件のレコードを取得
Person.take
# <Person id: 1, ...>
# SQL: SELECT * FROM people LIMIT 1
2件のレコードを取得
Person.take(2)
# [
# <Client id: 1, ...>,
# <Client id: 220, ...>
# ]
# SQL: SELECT * FROM people LIMIT 5
whereの後に使用
Person.where(["name LIKE '%?'", name]).take
ソースコード
条件に当てはまる値を全て取得
layout: page
説明
条件に当てはまるレコードを全て取得
使い方
モデル.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条件を上書き
layout: page
説明
既存の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
ソースコード
取得した値をグループ化
layout: page
説明
取得した値をグループ化
使い方
モデル.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")
ソースコード
取得した値を並び替え
layout: page
説明
取得したレコードを特定のキーで並び替える
使い方
モデル.order(:キー名 [ :並び順])
# または
モデル.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
ソースコード
既存の並び順を指定した条件に置き換える
layout: page
説明
既存の並び順を指定した条件に置き換える
使い方
モデル.reorder(ソート式)
並び順
並び順 | 説明 |
---|---|
ASC | 小さい方から大きい方に並ぶ(昇順) |
DESC | 大きい方から小さい方に並ぶ(降順) |
例
既存の並び順を指定した条件に置き換える
User.order('email DESC').reorder('id ASC')
# generated SQL has 'ORDER BY id ASC'
reorderより後の並び順は追加
User.order('email DESC').reorder('id ASC').order('name ASC')
ソースコード
取得した値を逆順に並び替え
layout: page
説明
取得した値を特定のキーで逆順に並び替える
使い方
モデル.reverse_order()
例
pagesテーブルをcategory_idを逆順で並び替える
Page.order("category_id").reverse_order
ソースコード
取得するレコード数の上限を指定
layout: page
説明
取得するレコード数の上限を指定
使い方
モデル.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
ソースコード
空のモデルを取得
layout: page
説明
空のモデルオブジェクトを取得
使い方
モデル.none()
例
空のモデルオブジェクトを取得
Post.none
ソースコード
特定のレコード位置から取得
layout: page
説明
特定のレコード位置から取得
使い方
モデル.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
ソースコード
読み込み専用で取得
layout: page
説明
読み込み専用としてモデルを取得
使い方
モデル.readonly(値)
例
読み込み専用としてモデルを取得
users = User.readonly
users.first.save
# ActiveRecord::ReadOnlyRecord: ActiveRecord::ReadOnlyRecord
ソースコード
取得した値を絞り込む
layout: page
説明
モデルから取得した値を元に絞り込む
使い方
モデル.having(条件式)
例
平均ageが30以上を取得
User.having(['AVG(age) >= ?', 30])
# SELECT * FROM users HAVING AVG(age) >= 30
ソースコード
関連するテーブルをまとめて取得
layout: page
説明
関連するテーブルをまとめて取得
使い方
モデル.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'))
ソースコード
複数のテーブルを結合して取得
layout: page
説明
複数のテーブルを結合して取得
使い方
モデル.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
ソースコード
適応した条件式を除外
layout: page
説明
モデルに適応した条件式を除外
使い方
モデル.except(条件式)
例
orderによる並び替えを除外
Page.order("category DESC").except(:order)
ソースコード
レコードが参照されるテーブルを指定
layout: page
説明
レコードが参照されるテーブルを指定
使い方
モデル.from(値 [, サブクエリ])
例
レコードが参照されるテーブルを指定
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
ソースコード
デフォルトのスコープを定義
layout: page
説明
デフォルトのスコープを定義
使い方
default_scope(条件式 [, ブロック])
例
whereを付与
class Article < ActiveRecord::Base
default_scope { where(published: true) }
end
Article.all
# SELECT * FROM articles WHERE published = true
ソースコード
モデルのレコード数を取得
layout: page
説明
モデルのレコード数を取得
使い方
モデル.count(カラム名)
例
テーブルの行数を計算
User.count
カラム指定
Person.count(:age)
groupで集計してから件数取得
Person.group(:city).count
# {Rome: 5, Paris: 3 }
ソースコード
カラムの平均値を計算
layout: page
説明
カラムの平均値を計算
使い方
モデル.average(カラム名)
例
平均を計算
Page.average(:count)
# SELECT AVG("pages"."count") AS avg_id FROM "pages"
ソースコード
カラムの最大値を計算
layout: page
説明
カラムの最大値を計算
使い方
モデル.maximum(カラム名 [, オプション])
オプション
オプション | 説明 |
---|---|
:conditions | 関数を条件に一致する行に制限 |
:include | 関連テーブルのデータを取得 |
:joins | テーブル結合 |
:order | 結果を並び替える |
:select | 集計に使う行を選択 |
:distinct | カラム内の重複した値を除いて、別個の値のみカウント |
:group | グルーピング命令 |
例
usersテーブルのageカラムの最大値を計算
User.maximum('age')
# SELECT MAX("users"."age") AS max_id FROM "users"
条件を指定
Account.maximum(:limit, :conditions => "limit > 50")
ソースコード
カラムの最小値を計算
layout: page
説明
カラムの最小値を計算
使い方
モデル.minimum(カラム名 [, オプション])
オプション
オプション | 説明 |
---|---|
:conditions | 関数を条件に一致する行に制限 |
:include | 関連テーブルのデータを取得 |
:joins | テーブル結合 |
:order | 結果を並び替える |
:select | 集計に使う行を選択 |
:distinct | カラム内の重複した値を除いて、別個の値のみカウント |
:group | グルーピング命令 |
例
usersテーブルのageカラムの最小値を計算
User.minimum('age')
# SELECT MIN("users"."age") AS min_id FROM "users"
条件を指定
Account.minimum(:limit, :conditions => "limit > 50")
ソースコード
主キーを全て取得
layout: page
説明
主キーを全て取得
使い方
モデル.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
ソースコード
カラムの合計値を計算
layout: page
説明
カラムの合計値を計算
使い方
モデル.sum(カラム名)
例
Ratingsテーブルのscoreカラムの合計を計算
Rating.sum(:score)
# SELECT SUM("Ratings"."score") AS sum_id FROM "Ratings"
ソースコード
指定したカラムのレコードの配列を取得
layout: page
説明
指定したカラムのレコードの配列を取得
使い方
モデル.pluck(カラム名 [, ...])
例
指定したカラムのレコードの配列を取得
Person.pluck(:id)
# SELECT people.id FROM people
# [1, 2, 3]
複数指定
Person.pluck(:id, :name)
# SELECT people.id, people.name FROM people
# [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
whereなどと一緒に使用
Person.where(age: 21).limit(5).pluck(:id)
# SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
# [2, 3]
ソースコード
取得済みのモデルから先頭のレコードの値を1件取得
layout: page
説明
whereやorderなどで取得済みのモデルから先頭のレコードの値を1件取得
relation.limit(1).pluck(*column_names).firstの省略形
使い方
pick(カラム名 [, ...])
例
先頭のレコードの値を1件取得
User.where(id: 1).pick(:name)
ソースコード
モデルを再取得
layout: page
説明
レコードを再取得
使い方
モデル.reload()
例
レコードを再取得
@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]]
ソースコード
データベースに保存
layout: page
説明
生成したモデルオブジェクトをデータベースに保存
使い方
モデル.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
ソースコード
データベースを更新
layout: page
説明
条件に一致するレコードを更新
使い方
モデル.update(ID, 属性)
例
条件に一致する属性を更新
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 | × |
ソースコード
条件に一致するレコードをすべて更新
layout: page
説明
条件に一致するレコードをSQLを直接実行して全て更新
updated_atとupdated_onは更新されない
使い方
モデル.update_all(カラム名: 値 [, オプション])
オプション
オプション | 説明 |
---|---|
:limit | 取得する件数 |
:order | 並び順 |
例
複数のデータを更新
User.update_all("name='A'", "category='B')
更新メソッドの種類
メソッド | バリデーションによるバリデーションの有無 |
---|---|
update | ○ |
update_all | × |
update_attributes | ○ |
update_attribute | × |
ソースコード
属性ハッシュを指定して更新
layout: page
説明
条件に一致するモデルオブジェクトを更新
使い方
モデル.update_attributes(属性名, 値)
例
複数の項目を一度に変更・保存するメソッド
@user.update_attributes(:name, "hoge")
更新メソッドの種類
メソッド | バリデーションの有無 |
---|---|
update | ○ |
update_all | × |
update_attributes | ○ |
update_attribute | × |
ソースコード
属性ハッシュを指定して更新
layout: page
説明
条件に一致するモデルオブジェクトを更新
バリデーションはスキップ
使い方
モデル.update_attribute(属性名, 値)
例
条件に一致するレコードを更新
@user.update_attribute(:name, "hoge")
更新メソッドの種類
メソッド | バリデーションによるバリデーションの有無 |
---|---|
update | ○ |
update_all | × |
update_attributes | ○ |
update_attribute | × |
ソースコード
複数レコードを一括登録
layout: page
説明
複数レコードを一括登録
直接SQLを実行するのでバリデーションやコールバックはスキップ
insert_all!はエラーの時に例外が発生
使い方
insert_all(属性 [, オプション])
オプション
オプション | 説明 |
---|---|
:returning | 戻り値の属性を指定(PostgreSQLのみ) |
:unique_by | 重複でスキップするカラムを指定(PostgreSQLとSQLiteのみ) |
例
複数レコードを一括登録
Book.insert_all([
{ id: 1, title: "Rework", author: "David" },
{ id: 1, title: "Eloquent Ruby", author: "Russ" }
])
ソースコード
ActiveRecordを使って指定した条件のレコードを削除
layout: page
説明
ActiveRecordを使って指定した条件のレコードを削除
dependentが設定されている場合は関連付けられたモデルも削除
使い方
モデル.destroy([条件])
例
指定した条件のレコードを削除
person.pets.destroy(Pet.find(1))
複数指定
person.pets.destroy(Pet.find(2), Pet.find(3))
ソースコード
指定した条件に一致するレコードをSQLを直接実行して削除
layout: page
説明
指定した条件に一致するレコードをSQLを直接実行して削除
関連付けられたモデルは削除しない
使い方
モデル.delete(条件)
例
レコードを削除
person.pets.delete(Pet.find(1))
# [<Pet id: 1, name: "Fancy-Fancy", person_id: 1>]
ソースコード
指定した条件に一致するレコードをSQLを直接実行して全て削除
layout: page
説明
指定した条件に一致するレコードをSQLを直接実行して全て削除
関連付けられたモデルは削除しない
使い方
モデル.delete_all([検索条件])
例
全てのレコードを削除
person.pets.delete_all
条件を指定
person.pets.delete_all("id > 0")
ソースコード
ActiveRecordを使って指定した条件の全てのレコードを削除
layout: page
説明
ActiveRecordを使って指定した条件の全てのレコードを削除
dependentが設定されている場合は関連付けられたモデルも削除
使い方
モデル.destroy_all([検索条件])
例
条件を指定して削除
Person.destroy_all("last_login < '2004-04-04'")
条件を指定して削除
Person.destroy_all(status: "inactive")
全て削除
Person.where(age: 0..18).destroy_all
ソースコード
条件式に一致しないものを取得
layout: page
説明
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'
ソースコード
汎用的なカラム処理
layout: page
説明
汎用的なカラム処理
使い方
モデル.calculate(処理方法, カラム名)
処理方法
処理方法 | 説明 |
---|---|
:count | カウント |
:sum | 合計値 |
:average | 平均値 |
:minimum | 最小値 |
:maximum | 最大値 |
例
pagesテーブルのカラム数
Page.calculate(:count, :all)
# SELECT COUNT(*) FROM "pages"
ソースコード
コピーを生成
layout: page
説明
モデルのコピーを生成
使い方
モデル.clone()
例
モデルのコピーを生成
user = User.first
new_user = user.clone
user.name # "Bob"
new_user.name = "Joe"
user.name # "Joe"
ソースコード
特定のレコードをインクリメント
layout: page
説明
特定のレコードをインクリメント
使い方
モデル.increment(レコード, インクリメントする値)
例
Pageテーブルのnumレコードを3ずつインクリメント
Page.increment(:num, 3)
ソースコード
属性変更を取得
layout: page
説明
変更点を取得
使い方
モデル.changed()
例
person.changed # []
person.name = 'bob'
person.changed # ["name"]
ソースコード
保存していないレコードがあるかチェック
layout: page
説明
保存していないレコードがあるかチェック
使い方
モデル.changed?()
例
保存していないレコードがあるかチェック
@user.changed?
ソースコード
カラム情報の取得
layout: page
説明
指定したカラム名のオブジェクトを取得
使い方
モデル.column_for_attribute([カラム名])
例
pageのtitleを取得
@page = Page.find(1)
@page.column_for_attribute(:title)
ソースコード
すべての属性名を配列で取得
layout: page
説明
すべての属性名を配列で取得
使い方
モデル.attribute_names()
例
すべての属性名を配列で取得
Person.attribute_names
# ["id", "created_at", "updated_at", "name", "age"]
ソースコード
属性の値に似た文字列を取得
layout: page
説明
属性の値に似た文字列を取得
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]"
ソースコード
指定した属性が存在するかチェック
layout: page
説明
指定した属性が存在するかチェック
使い方
モデル.attribute_present?(属性名)
例
person.attribute_present?(:title)
# true
ソースコード
全てのモデルオブジェクトと属性を取得
layout: page
説明
全てのモデルオブジェクトと属性を取得
使い方
モデル.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}
ソースコード
型に変更前のハッシュを取得
layout: page
説明
型に変更前のハッシュを取得
使い方
モデル.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}
ソースコード
タイムスタンプの自動保存を停止
layout: page
説明
タイムスタンプの自動保存を停止
使い方
ActiveRecord::Base.record_timestamps = false
モデルに指定した属性が存在するかチェック
layout: page
説明
モデルに指定した属性が存在するかチェック
使い方
モデル.has_attribute?(属性)
例
指定した属性が存在する場合
person.has_attribute?(:name)
# true
指定した属性が存在しない場合
person.has_attribute?(:nothing)
# false
ソースコード
型に変更前のハッシュを取得
layout: page
説明
型に変更前のハッシュを取得
使い方
モデル.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"
ソースコード
指定したモデルオブジェクトの属性が存在するかチェック
layout: page
説明
指定したモデルの属性が存在するかチェック
使い方
モデル.attribute_method?(属性)
例
true
User.attribute_method?(:name)
# true
false
User.attribute_method?(:age)
# false
ソースコード
レコードの存在を確認
layout: page
説明
指定したレコードが存在するか
使い方
モデル.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
ソースコード
取得する時のSQLを表示
layout: page
説明
取得する時のSQLを表示
使い方
モデル.to_sql()
例
取得する時のSQLを表示
User.where(name: 'Oscar').to_sql
# SELECT "users".* FROM "users" WHERE "users"."name" = 'Oscar'
ソースコード
新しいレコードかチェック
layout: page
説明
新しいレコードかチェック
使い方
モデル.new_record?()
例
新しいレコードかチェック
User.new_record?
ソースコード
保存済みかチェック
layout: page
説明
保存済みかチェック
使い方
モデル.persisted?()
例
保存済みチェック
User.persisted?
ソースコード
削除済みかチェック
layout: page
説明
削除済みかチェック
使い方
モデル.destroyed?()
例
基本形(オプションなし)
@user.destroyed?
ソースコード
複数カラムをまとめる
layout: page
説明
複数カラムを擬似的に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 }
ソースコード
モデルが空かチェック
layout: page
説明
モデルが空かチェック
使い方
モデル.any?([ブロック])
例
基本的な使い方
class Person < ActiveRecord::Base
has_many :pets
end
person.pets.count # 0
person.pets.any? # false
person.pets << Pet.new(name: 'Snoop')
person.pets.count # 0
person.pets.any? # true
ブロック
person.pets
# [#<Pet name: "Snoop", group: "dogs">]
person.pets.any? do |pet|
pet.group == 'cats'
end
# false
person.pets.any? do |pet|
pet.group == 'dogs'
end
# true
ソースコード
例外が発生するかチェック
layout: page
説明
例外が発生するかチェック
使い方
モデル.instance_method_already_implemented?(属性)
例
例外が発生する
Person.instance_method_already_implemented?(:save)
# ActiveRecord::DangerousAttributeError: save is defined by ActiveRecord
例外が発生しない
Person.instance_method_already_implemented?(:name)
# false
ソースコード
URLのidの部分にid以外のものを指定
layout: page
説明
URLのidの部分にid以外のものを指定
使い方
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
例
URLのidの部分にid以外のものを指定
class User < ActiveRecord::Base
def to_param # overridden
name
end
end
ソースコード
update_atを更新
layout: page
説明
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
ソースコード
関連モデルオブジェクトから新しい属性を作成
layout: page
説明
関連モデルオブジェクトから新しい属性を作成
「nil」を引数で渡すと属性をリセット
使い方
モデル.create_with(属性)
例
関連オブジェクトから新しいモデルを作成
users = users.create_with(name: 'DHH')
users.new.name
# 'DHH'
属性をリセット
users = users.create_with(nil)
users.new.name
# 'Oscar'
ソースコード
左外部結合を使ってすべてのレコードを取得
layout: page
説明
左外部結合を使ってすべてのレコードを取得
使い方
モデル.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"
ソースコード
scopeを使ってモデルを拡張
layout: page
説明
モジュールやブロックメソッドを引数に与えて、モデルを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])
ソースコード
モデルをハッシュ形式のJSONに変換
layout: page
説明
モデルをハッシュ形式のJSONに変換
使い方
モデル.as_json([オプション])
オプション
オプション | 説明 | デフォルト値 |
---|---|---|
: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" } ] }
ソースコード
モデルの変更前と変更後の値をハッシュで取得
layout: page
説明
モデルの変更前と変更後の値をハッシュで取得
使い方
モデル.changes()
例
person.changes # {}
person.name = 'bob'
person.changes # {name: ["bill", "bob"]}
ソースコード
全てのバリデーションをクリア
layout: page
説明
全てのバリデーションをクリア
使い方
モデル.clear_validators!()
例
全てのバリデーションをクリア
Person.clear_validators!
ソースコード
SQL文を使って件数を取得
layout: page
説明
SQL文を使って件数を取得
使い方
count_by_sql(SQL文)
例
Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
# 12
ソースコード
テーブルに外部キー制約が存在するかチェック
layout: page
説明
テーブルに外部キー制約が存在するか
使い方
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")
ソースコード
分割してレコードを取得して処理
layout: page
説明
分割してレコードを取得して処理
デフォルトで1000件ずつ処理
使い方
モデル.in_batches([オプション]) do |i|
処理内容
end
オプション
オプション | 説明 | デフォルト値 |
---|---|---|
:of | 同時処理数 | 1000 |
:load | ロードするか | false |
:start | 処理開始位置 | |
:finish | 終了するときのキー | |
:error_on_ignore | 例外を発生させる |
例
分割してレコードを取得して処理
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で値を返す
ソースコード
指定されたテーブルのインデックスを配列で取得
layout: page
説明
指定されたテーブルのインデックスを配列で取得
使い方
indexes(テーブル名)
例
indexes(:pages)
ソースコード
関連するモデルの左外部結合
layout: page
説明
関連するモデルの左外部結合
使い方
モデル.left_outer_joins(モデル名)
例
関連するモデルの左外部結合
User.left_outer_joins(:posts)
# SELECT "users".* FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
ソースコード
主キーを設定して複数のレコードを追加
layout: page
説明
全てのレコード数を取得
使い方
モデル.concat(モデル)
例
全てのレコード数を取得
person.pets.count
# => 3
ソースコード
重複のない値を取得
layout: page
説明
重複のない値を取得
使い方
モデル.distinct([重複を削除するか])
例
重複のない値を取得
User.select(:name).distinct
重複も削除
User.select(:name).distinct(false)
ソースコード
OR条件式
layout: page
説明
OR条件式
使い方
モデル.or(条件式)
例
OR条件式
Post.where("id = 1").or(Post.where("author_id = 3"))
# SELECT `posts`.* FROM `posts` WHERE ((id = 1) OR (author_id = 3))
ソースコード
条件式を削除
layout: page
説明
条件式を削除
使い方
モデル.unscope(条件式)
例
条件式を削除
User.order('email DESC').unscope(:order) == User.all
ソースコード
モデルが保存される前に変更された値をハッシュで取得
layout: page
説明
保存される前に変更された値をハッシュで取得
使い方
モデル.previous_changes()
例
保存される前に変更された値をハッシュで取得
person.name # "bob"
person.name = 'robert'
person.save
person.previous_changes # {name: ["bob", "robert"]}
ソースコード
カラムを指定して取得
layout: page
説明
カラムを指定して取得
使い方
モデル.select(カラム名)
例
カラムを指定して取得
person.pets.select(:id, :name)
# [
# <Pet id: 1, name: "Fancy-Fancy">,
# <Pet id: 2, name: "Spook">,
# <Pet id: 3, name: "Choo-Choo">
# ]
ブロックで指定
person.pets.select { |pet| pet.name =~ /oo/ }
# [
# <Pet id: 2, name: "Spook", person_id: 1>,
# <Pet id: 3, name: "Choo-Choo", person_id: 1>
# ]
ソースコード
指定した条件以外の条件をクエリから削除
layout: page
説明
指定した条件以外の条件をクエリから削除
使い方
モデル.only(条件)
例
条件式を削除
Post.order('id asc').only(:where)
複数指定
Post.order('id asc').only(:where, :order)
ソースコード
トランザクション
layout: page
説明
分割不可能な複数のレコードの更新を1つの単位にまとめて処理すること
特徴
- データベース内の情報の整合性を保つための手段
- 複数のデータベースにまたがる分散トランザクションはサポートしていない
- 使用するにはデータベースがトランザクションをサポートしていることが必要
使い方
ActiveRecord::Base.transaction do
例外が発生するかもしれない処理
end
例外が発生しなかった場合の処理
resque => e
例外が発生した場合の処理
モデル.transaction do
例外が発生するかもしれない処理
end
例外が発生しなかった場合の処理
resque => e
例外が発生した場合の処理
- ブロック内のすべての処理が正常に行われた場合に保存
- エラーが発生した場合は、ロードバック
例
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
ソースコード
データベースのロック
layout: page
楽観的ロック
あらかじめカラムのロックバージョンを記録しておき、更新時にロックバージョンが変わっていないことをバリデーションして保存
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でのみ利用可能