こんにちは、かつコーチです。
Railsでデータを扱おうとすると、Post.create や Post.find といったコードを目にします。
SQLを書いていないのに、なぜデータベースを操作できるのか。
最初は不思議に感じるところだと思います。
この記事では、Railsのデータベース操作を支えるActiveRecord(ORM:Object-Relational Mapping、Rubyのオブジェクトとデータベースのテーブルを対応づける仕組みです)の基本と、create / find / update / destroy という基本操作(CRUD:Create・Read・Update・Deleteの頭文字です)を解説します。
読み終えるころには、モデルを使ったデータ操作に自信が持てるようになっているはずです。
ActiveRecordとは何か(ORMの説明)
テーブルとモデルが1対1で対応する
ActiveRecordの最大の特徴は、データベースのテーブルとRubyのクラス(モデル)が自動的に対応づけられることです。
例えば posts テーブルがあれば、Post モデルが自動的にそのテーブルを操作できるようになります。
# app/models/post.rb
class Post < ApplicationRecord
end
このクラスにはメソッドを1つも書いていませんが、title や body といったカラムに対応するメソッドが自動的に使えるようになります。
これは、テーブルの構造(カラム名や型)をActiveRecordが実行時に読み取り、それに応じたメソッドを動的に用意してくれるためです。
SQLを書かずに操作できる理由
先ほどのマイグレーションで作成した posts テーブルに対して、以下のようにRubyのコードだけでデータを操作できます。
# rails consoleでの実行例
Post.create(title: "初めての投稿", body: "ActiveRecordを学び中です")
このコードは、裏側で以下のようなSQLに変換されて実行されています。
INSERT INTO posts (title, body, created_at, updated_at)
VALUES ('初めての投稿', 'ActiveRecordを学び中です', '2026-08-22 01:00:00', '2026-08-22 01:00:00');
SQLを直接書く必要がないので、データベースの方言(MySQLとPostgreSQLの文法の違いなど)を意識せずにコードを書けるのがActiveRecordの大きなメリットです。
create/find/update/destroyの基本
Create:データを作成する
# 方法1:createで作成と保存を同時に行う
post = Post.create(title: "Railsの学習記録", body: "今日はActiveRecordを学んだ")
# 方法2:newで組み立ててからsaveで保存する
post = Post.new(title: "Railsの学習記録", body: "今日はActiveRecordを学んだ")
post.save
create は「組み立て」と「保存」を1回で行い、new + save は2段階に分けて行います。
バリデーション(データの妥当性チェック)でエラーになる可能性がある場合は、new + save の形で、保存前にデータを加工したいケースに向いています。
Read:データを取得する
# 全件取得
posts = Post.all
# IDを指定して1件取得(見つからないと例外が発生する)
post = Post.find(1)
# 条件を指定して1件取得(見つからなければnilが返る)
post = Post.find_by(title: "Railsの学習記録")
# 条件を指定して複数件取得
posts = Post.where(published: true)
# 並び替え
posts = Post.order(created_at: :desc)
find と find_by の挙動の違いは、初心者がよく混同するポイントです。
find はIDで検索し、見つからなければエラーになります。
find_by は条件で検索し、見つからなくても nil を返すだけで、エラーにはなりません。
Update:データを更新する
post = Post.find(1)
post.update(title: "更新後のタイトル")
update メソッドは、値の書き換えと保存を同時に行います。
複数の属性を同時に変更したいときも、ハッシュでまとめて渡せます。
post.update(title: "新タイトル", body: "新しい本文")
Destroy:データを削除する
post = Post.find(1)
post.destroy
destroy を呼ぶと、対象のレコードがデータベースから削除されます。
コントローラの destroy アクションでは、この処理を呼び出すのが定番の形です。
# app/controllers/posts_controller.rb
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to posts_path, notice: "投稿を削除しました。", status: :see_other
end
よくあるつまずきポイント・エラー対処
ActiveRecord::RecordNotFound でアプリが落ちる
私が実際にやってしまった失敗です。
存在しないIDを find で検索したときに、エラー画面がそのままユーザーに表示されてしまいました。
ActiveRecord::RecordNotFound in PostsController#show
Couldn't find Post with 'id'=999
❌ Before:findの例外を考慮していない
# app/controllers/posts_controller.rb
def show
@post = Post.find(params[:id])
end
存在しないIDでアクセスされると、この時点で例外が発生し、そのままエラー画面になってしまいます。
✅ After:rescue_fromで例外を捕捉し、わかりやすい画面に誘導する
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
private
def render_not_found
redirect_to root_path, alert: "指定されたデータが見つかりませんでした。"
end
end
find は「必ず見つかる前提」の操作なので、見つからない可能性がある場面では、例外処理をセットで用意しておくことが大切です。
saveの戻り値を確認せずに次の処理に進んでしまう
save はバリデーションに失敗すると false を返しますが、例外は発生しません。
これに気づかずハマったことがあります。
❌ Before:saveの結果を確認せず、常に成功したものとして扱う
def create
@post = Post.new(post_params)
@post.save
redirect_to @post, notice: "投稿を作成しました。"
end
バリデーションエラーで保存に失敗しても、この書き方だと @post.save の結果を無視しているため、@post にIDが振られておらず、redirect_to @post の時点でエラーになってしまいます。
✅ After:saveの戻り値を条件分岐で確認する
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post, notice: "投稿を作成しました。"
else
render :new, status: :unprocessable_entity
end
end
save は成功・失敗を表す真偽値を返す設計になっているので、必ず条件分岐で受け止める習慣をつけましょう。
save!(末尾に ! がついたメソッド)を使うと、失敗時に例外を発生させることもできます。
用途に応じて使い分けてください。
まとめ
この記事のポイント
- ActiveRecordは、テーブルとRubyのクラス(モデル)を自動的に対応づけるORM
- SQLを直接書かなくても、Rubyのメソッドでデータベースを操作できる
createは組み立てと保存を同時に行い、new+saveは2段階に分けて行うfindはIDで検索して見つからなければ例外、find_byは条件検索でnilを返すsaveの戻り値は必ず確認し、失敗時の処理も用意しておく
次に読むべき記事
- マイグレーションの基本:テーブルを作成・変更する
- バリデーションの基本
- アソシエーション(has_many・belongs_to)の書き方
タグ: Ruby on Rails, 初心者向け, データベース