初中級者の悩み
RailsチュートリアルやYouTubeのハンズオンなどを終えた頃の駆け出しエンジニアが直面するであろう悩み。
「とりあえず基本的なCRUDアプリは作れるようになったけど、Railsの裏側の仕組みはよく分からない...」
「教材で言われるがままコードを書いてるけど、自分一人で書けるかというと不安...」
こうした悩みを解決するために、今回はRailsのDB・Modelに関わるActiveRecordについて解説します。
私自身も、Railsを初めて3年目くらいの時にやっと理解した(ずっと見て見ぬふりをしていた)という感じでした()。だって理解してなくてもアプリは作れちゃうから。
しかし実務でDBを適切に扱うためには、ActiveRecordに対する理解は必須と言えます。
ActiveRecordを理解することで、例えば、
-
N+1問題の解決:
includesやjoinsを適切に使い分けることで、アプリケーションのレスポンス速度を劇的に改善できるようになる - 遅延評価 (Lazy Evaluation) の活用: クエリがいつ実行されるかをコントロールし、メモリ使用量を最小限に抑える高度な実装ができるようになる
- スコープ (Scopes) による再利用: よく使う検索条件(例:User.active)を定義し、DRY(コードの重複を避ける)な設計が可能になる
といった様々な恩恵を受けられます!(小難しいと思った方もそのままでOK)
この記事を通してActiveRecordについて解像度を上げ、railsエンジニアとしてレベルアップしていきましょう!
ActiveRecordとは
公式ドキュメントでは、Active Recordは以下のように説明されます。
Active Recordとは、MVCで言うところのM、つまりモデルの一部であり、データとビジネスロジックを表現するシステムの階層です。Active Recordは、データベースに恒久的に保存される必要のあるビジネスオブジェクトの作成と利用を円滑に行なえるようにします。
Railsガイド『Active Recordの基礎』より
この説明だけで理解できた方はこの先読まなくてOKです。
逆に
「公式ドキュメントってなんでこう物事を難しげに書くんだろう...」
と思った方は是非この先を読み進めてください。
ActiveRecordとは翻訳機
簡単に言うと、ActiveRecordとは
Rubyのコードでデータベースを操作するための仕組み
です。
例えば、私たちは普段
@user = User.find(params[:id])
のようなコードを書くだけで、idパラメータをもとにユーザーの情報をDBから取得できます。
しかしよく考えてみると、DBを操作できるのはSQLという言語であって、Rubyではないはずです。
では、なぜRubyのコードを書くだけでDBを操れるのでしょうか。
それは「ActiveRecordという翻訳の仕組みがあるから」です!
例えば、
users_controller.rbのcreateアクションにおいて、
@user = User.create(name: "Tanaka", email: "tanaka@example.com")
というコードが実行されると、裏側でRubyの.createはActiveRecordによって
INSERT INTO users (name, email) VALUES ('Tanaka', 'tanaka@example.com');
というSQL文に自動翻訳されることで、結果として命令通りuserのレコードをDBにINSERT(保存)ができます。
基本的なCRUD操作における翻訳の例は以下のような感じ。
CREATE
-
↓ ActiveRecordの翻訳
@user = User.save(name: "Tanaka", email: "tanaka@example.com")INSERT INTO users (name, email) VALUES ('Tanaka', 'tanaka@example.com');
READ
-
↓ ActiveRecordの翻訳
@user = User.find(params[:id])SELECT * FROM users WHERE id = ? LIMIT 1;
-
↓ ActiveRecordの翻訳
@user = User.find_by(email: "a@ex.com")SELECT * FROM users WHERE email = 'a@ex.com' LIMIT 1;
-
↓ ActiveRecordの翻訳
@users = User.allSELECT * FROM users;
UPDATE
-
↓ ActiveRecordの翻訳
@user.update(name: "Sato")UPDATE users SET name = 'Sato', updated_at = '~~~', WHERE id = ?;
DELETE
-
↓ ActiveRecordの翻訳
@user.destroyDELETE FROM users WHERE id = ?;
このように、SQLを書かなくても勝手にRubyを翻訳してくれて、そのおかげで私たちはデータベース操作ができるというわけです。
ちなみに「SQLを直接書かずにデータベースにアクセスしたりできる技術」を「Object Relational Mapping(ORM)」と呼びます。
オブジェクト/リレーショナルマッピング(一般にORMと呼ばれます)は、プログラミング言語のリッチなオブジェクトをリレーショナルデータベース管理システム(RDBMS)のテーブルに接続する技術です。Railsアプリケーションの場合、これらはRubyオブジェクトです。ORMによって、SQLステートメントを直接記述せずに、Rubyオブジェクトの属性やオブジェクト間の関係をデータベースに手軽に保存したり、データベースから取得したりできます。ORMによって、作成する必要があるデータベースアクセスコードの量は一般に最小限で済むようになります。
ActiveRecordの利点
このほかにも、ActiveRecordがあることのメリットとして、
1. データベース非依存
RailsではPostgreSQL, MySQL, SQLiteなどといった様々なRDBMSと接続できますが、これらはSQLの構文が微妙に異なります。そのため、開発者は通常、RDBMSの種類に合わせてSQLを書き換えないといけません。
しかし、ActiveRecordは、どのRDBMSを使うにしてもその方言(構文の微妙な違い)を吸収してくれます。
エンジニアがやるべきなのは設定の切り替えのみ。
database.yml を書き換えるだけで、アプリケーションコードを1行も修正せずにDBを変更できます!
development:
<<: *default
database: storage/development.sqlite3
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: storage/test.sqlite3
# Store production database in the storage/ directory, which by default
# is mounted as a persistent Docker volume in config/deploy.yml.
production:
primary:
<<: *default
database: storage/production.sqlite3
cache:
<<: *default
database: storage/production_cache.sqlite3
migrations_paths: db/cache_migrate
queue:
<<: *default
database: storage/production_queue.sqlite3
migrations_paths: db/queue_migrate
cable:
<<: *default
database: storage/production_cable.sqlite3
migrations_paths: db/cable_migrate
2. ビジネスロジックの集約と整合性
Railsではモデルにバリデーションやコールバック処理、アソシエーションなどを設定することができますが、これらもActiveRecordの恩恵です。
class User < ApplicationRecord
# --- アソシエーション (Associations) ---
# 1対多の関係
has_many :posts, dependent: :destroy
# 多対1の関係(任意項目にする場合は optional: true)
belongs_to :group, optional: true
# --- バリデーション (Validations) ---
# 名前: 必須、20文字以内
validates :name, presence: true, length: { maximum: 20 }
# メールアドレス: 必須、一意性、フォーマットの確認
validates :email, presence: true, uniqueness: true, format: { with: URI::MailTo::EMAIL_REGEXP }
# パスワード: 新規作成時のみ6文字以上
validates :password, length: { minimum: 6 }, if: :password_required?
# --- コールバック (Callbacks) ---
# 保存の前にメールアドレスを小文字に変換する(一意性の担保)
before_save :downcase_email
# 登録後にウェルカムメールを送信する(仮想のメソッド)
after_create :send_welcome_email
private
def downcase_email
self.email = email.downcase if email.present?
end
def send_welcome_email
# UserMailer.welcome_email(self).deliver_now など
puts "#{self.name}さんにウェルカムメールを送信しました"
end
def password_required?
new_record? || password.present?
end
end
バリデーションがあることで、DBに保存する前後の処理をモデル側で「ルール」として定義できます。DB自体にも制約を設定できますが、DBにアクセスする前に不正なデータを弾くため、無駄なSQLの発行を抑えられます。
また、コールバックにより「保存の直前にパスワードを暗号化する」といった処理をフック(予約)できます。
アソシエーションを使ったデータの取得も、ActiveRecordによって簡単にできるようになっています。
1対多や多対多の関係をモデルの記述 has_many: ~~ や belongs_to などを書くだけで簡単に定義でき、例えば user.posts と書くだけで関連するデータを芋づる式に取得できます。
3. セキュリティの自動担保
生のSQLを自分で組み立てると、SQLインジェクションという重大な脆弱性を作り込みがちですが、Active Recordはこれを防ぎます。
プレースホルダの自動利用: where("name = ?", params[:name]) のように書くことで、悪意のある入力値を自動的にサニタイズ(無害化)してくれます。
4. 強力なエコシステムと最適化ツール
Railsの標準であるため、周辺ツールや知識が豊富です。
遅延評価: 必要になるまでSQLを発行しない仕組みにより、無駄な通信を抑えられます。
N+1問題の検知: bullet などのGemを使えば、パフォーマンス低下の原因となる無駄なクエリを簡単に見つけ出し、includes メソッドなどで即座に最適化できます。
まとめ
ActiveRecordの翻訳機としての役割、そしてActiveRecordのメリットをなんとなくイメージできたでしょうか?
ここまでイメージできた方は、それを踏まえてコードを見直してみると発見があると思います。
より詳しくコードベースで理解したい人は、別の記事もぜひご覧ください。
最後まで読んでいただきありがとうございました!


