【Rails】presence: true にしつつ、空文字を許容したい
こんにちは!
ラブグラフエンジニアのひろです。
今回はバリデーションについてです。
「値が入っていて欲しいので presence: true にしたいが、既存のデータには "" が入っている」
という状況に対応できるバリデーションを作っていきます。
問題の説明
例えば、 users
テーブルの name
カラムを必須にしたいケースで考えてみましょう。
単純に考えれば presence: true
を追加することになると思います。
これにより新しく User を作るとき、 name が指定されていなければバリデーションエラーが発生してくれます。
class User < ApplicationRecord
validates :name, presence: true
end
user = User.new(name: nil)
user.valid?
# => false
user.errors[:name]
# => ["can't be nil"]
しかし、すでに存在する User の name カラムに ""(空文字) が入っているとどうなるでしょうか?
presence: true は内部的に .blank?
を使ってバリデーションをおこなうため、既存 User にもバリデーションエラーが発生するようになってしまいます。
existing_user = User.find_by(name: "")
existing_user.valid?
# => false
existing_user.errors[:name]
# => ["can't be blank"]
allow_blank との併用
では presence: true
と、 空文字や nil を許可する allow_blank: true を併用するとどうなるでしょうか?
class User < ApplicationRecord
validates :name, presence: true, allow_blank: true
end
実際に Rails コンソールで確認してみましょう。
user = User.new(name: "")
user.valid?
# => true
user.name = nil
user.valid?
# => true
この設定では、 allow_blank: true
が優先され、 presence: true
があるにもかかわらず、空文字や nil も登録することができるようになってしまいます。
nil だけを弾くバリデーションの実装方法
され、それでは今回のケースに対応するバリデーションを作っていきましょう。
空文字を許容して、nil だけを弾くには、カスタムバリデーションを使います。
以下は User
モデルでのカスタムバリデーションの例です。
class User < ApplicationRecord
validate :name_cannot_be_nil
private
def name_cannot_be_nil
errors.add(:name, "can't be nil") if name.nil?
end
end
このカスタムバリデーションでは、 name
が nil
の場合にだけエラーを追加します。
空文字は許容されるので、 name が空文字の既存 User を更新する際に影響は与えません。
実装後の確認方法
この実装がちゃんと動くかを確認するために、 Rails コンソールでチェックします。
user = User.new(name: "")
user.valid?
# => true
user.name = nil
user.valid?
# => false
name
が空文字の場合にはバリデーションエラーが発生せず、 nil
の場合には発生することが確認できました。
まとめ
この記事では、 presence: true
を使った場合に、空文字もバリデーションエラーになる問題について説明し、nil だけを弾くカスタムバリデーションの方法を紹介しました。
また、 presence: true
と allow_blank: true
の併用が意味をなさないことも理解してもらえたと思います。
バリデーションの設定で悩んだことがあるエンジニアの皆さんに、役立つ情報になれば嬉しいです。
Discussion