こんにちは、かつコーチです。
フォームからデータを保存する機能を作っていると、「空欄のまま保存されてしまう」「同じメールアドレスで何人も登録できてしまう」といった困りごとに出会います。
こうした不正なデータの保存を防ぐ仕組みが、Railsのバリデーション(データを保存する前に内容が正しいかチェックする仕組み)です。
今回は、ユーザー登録フォームを例に、よく使うバリデーションの書き方と、valid?メソッドやエラー情報の確認方法を解説します。
バリデーションの基本の書き方
presence:空欄を許さない
もっともよく使うのが、値が空でないことを保証するpresenceバリデーションです。
# app/models/user.rb
class User < ApplicationRecord
validates :name, presence: true
validates :email, presence: true
end
これで、nameまたはemailが空のままレコードを保存しようとすると、保存が失敗するようになります。
user = User.new(name: "", email: "test@example.com")
user.save
# => false(保存されない)
length:文字数の範囲を指定する
パスワードの最低文字数や、名前の文字数上限などを指定するにはlengthを使います。
class User < ApplicationRecord
validates :name, presence: true, length: { maximum: 30 }
validates :password, length: { minimum: 8 }, allow_nil: true
end
minimumは最小文字数、maximumは最大文字数、in: 8..20のように範囲で指定することも可能です。
uniqueness:重複を防ぐ
メールアドレスの重複登録を防ぐにはuniquenessを使います。
class User < ApplicationRecord
validates :email, presence: true, uniqueness: true
end
ただし、uniquenessだけではアプリケーション側のチェックしか行われません。
同時に複数のリクエストが飛んできた場合、わずかなタイミングのずれで重複登録が起きる可能性があるため、データベース側にも一意インデックス(重複した値を許さない設定)を張っておくのが安全です。
# db/migrate/xxxxxx_add_unique_index_to_users_email.rb
class AddUniqueIndexToUsersEmail < ActiveRecord::Migration[8.0]
def change
add_index :users, :email, unique: true
end
end
正規表現でメール形式をチェックする
メールアドレスの形式を簡易的にチェックするにはformatを使います。
class User < ApplicationRecord
validates :email,
presence: true,
uniqueness: true,
format: { with: URI::MailTo::EMAIL_REGEXP }
end
URI::MailTo::EMAIL_REGEXPはRuby標準ライブラリに用意されているメール形式チェック用の正規表現で、自分で正規表現を組む必要がないため安心して使えます。
よくあるつまずきポイント・エラー対処
saveの戻り値を確認せずに次の処理へ進んでしまう
私が初心者の頃によくやってしまった失敗が、saveの戻り値をチェックせずに処理を進めてしまうことです。
❌ Before:save!を使わずエラーに気づけない
def create
@user = User.new(user_params)
@user.save
redirect_to @user
end
バリデーションに引っかかって保存が失敗しても、saveはfalseを返すだけで例外を投げません。
そのため上のコードでは、保存されていないのにredirect_to @userが実行され、@user.idがnilのままURLが組み立てられて、次のような画面に遷移してしまいました。
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User without an ID
保存が失敗したことに気づかないまま、まったく別のエラー画面が表示されるという、原因を追いにくいバグでした。
✅ After:valid?とerrorsで保存前にチェックする
def create
@user = User.new(user_params)
if @user.save
redirect_to @user
else
render :new, status: :unprocessable_entity
end
end
saveがfalseを返した場合は、render :newで入力フォームに戻し、@user.errorsにエラー内容が入っている状態でビューに渡します。
ビュー側では、errorsオブジェクトから具体的なエラーメッセージを取り出せます。
<% if @user.errors.any? %>
<ul>
<% @user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
valid?とinvalid?の使い分け
保存はせずに、事前にバリデーションだけ実行したい場合はvalid?を使います。
user = User.new(name: "", email: "test@example.com")
user.valid?
# => false(バリデーション実行後、falseが返る)
user.errors.full_messages
# => ["Nameを入力してください"]
valid?を呼び出すとerrorsオブジェクトにエラー内容が格納されるため、saveを呼ばなくてもエラー内容を確認できます。
逆に、バリデーションが通っているかを確認したいだけならinvalid?(valid?の逆で、エラーがある場合にtrueを返す)を使うと、条件分岐が読みやすくなる場合もあります。
まとめ
この記事のポイント
presenceは空欄を許さない、lengthは文字数、uniquenessは重複防止のバリデーションuniquenessだけに頼らず、データベース側にも一意インデックスを設定するsaveは失敗時にfalseを返すだけなので、戻り値を必ずチェックするvalid?を呼ぶとerrorsオブジェクトにエラー内容が格納される- エラーメッセージは
errors.full_messagesで取り出してビューに表示する
次に読むべき記事
→ 次の記事:アソシエーション(has_many・belongs_to)の書き方
タグ: Ruby on Rails, 初心者向け, データベース