今日はバリデーションをカスタマイズできることを知りました。
正直、指摘いただいた後に、何故気づかなかったのか不思議でした。
欲しいメソッド・機能はすでに先人が作ってくれていることがほとんどです。
Railsガイドを先に見る癖が最近薄れていたなと反省しました。
バリデーションの簡単なカスタマイズを試してみます。
まずは、モデルを用意。
❯ rails generate model Person invoke active_record create db/migrate/20231107110115_create_people.rb create app/models/person.rb invoke test_unit create test/models/person_test.rb create test/fixtures/people.yml
マイグレーションファイルに「名前」を設定。
class CreatePeople < ActiveRecord::Migration[7.0] def change create_table :people do |t| t.string :name t.timestamps end end end
マイグレーションを実行。
❯ rails db:migrate == 20231107110115 CreatePeople: migrating ===================================== -- create_table(:people) -> 0.0014s == 20231107110115 CreatePeople: migrated (0.0014s) ============================
モデルには、名前に空文字が登録できないようバリデーション(presence:true) を設定します。
class Person < ApplicationRecord validates :name, presence: true end
コンソールでレコードを作成し、バリデーションの有効性を確かめます。
valid?
メソッドを使うとバリデーションがトリガされます。
オブジェクトにエラーがない場合はtrue
を返し、エラーの場合はfalse
を返します。
❯ rails c Loading development environment (Rails 7.0.4.2) >> Person => Person (call 'Person.connection' to establish a connection) >> Person.create(name: "John Doe").valid? TRANSACTION (0.0ms) begin transaction Person Create (0.7ms) INSERT INTO "people" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", "John Doe"], ["created_at", "2023-11-07 11:09:17.513911"], ["updated_at", "2023-11-07 11:09:17.513911"]] TRANSACTION (0.6ms) commit transaction => true >> Person.create(name: nil).valid? => false
上記から、確かに空文字の場合のバリデーションが有効であることがわかります。
続いて年齢を追加してみます。
❯ rails generate migration AddAgeToPerson age:integer invoke active_record create db/migrate/20231107111135_add_age_to_person.rb ❯ rails db:migrate == 20231107111135 AddAgeToPerson: migrating =================================== -- add_column(:people, :age, :integer) -> 0.0139s == 20231107111135 AddAgeToPerson: migrated (0.0140s) ==========================
年齢が10歳以上である場合に限り登録できるように、モデルにバリデーションを追加します。
class Person < ApplicationRecord validates :name, presence: true validate :age_must_be_at_least_10 private def age_must_be_at_least_10 if age.present? && age < 10 errors.add(:age, "は10歳以上である必要があります") end end end
コンソールで追加したバリデーションの有効性を確かめます。
❯ rails c Loading development environment (Rails 7.0.4.2) >> Person.create(name: "John Doe", age:12).valid? TRANSACTION (0.0ms) begin transaction Person Create (1.4ms) INSERT INTO "people" ("name", "created_at", "updated_at", "age") VALUES (?, ?, ?, ?) [["name", "John Doe"], ["created_at", "2023-11-07 11:25:53.861562"], ["updated_at", "2023-11-07 11:25:53.861562"], ["age", 12]] TRANSACTION (0.6ms) commit transaction => true >> Person.create(name: "John Doe", age:9).valid? => false >>
12歳ではtrue、10歳ではfalseが返却されました。
意図したとおりできています。
ここで、空文字を登録できないnameのバリデーションの方は有効な状態で、名前に【.exc】が含まれていたら10歳未満でも登録できるように変更してみます。
バリデーションのメソッドの後に、unlessで条件を指定します。
class Person < ApplicationRecord validates :name, presence: true validate :age_must_be_at_least_10, unless: -> { name.include?(".exc") } private def age_must_be_at_least_10 if age.present? && age < 10 errors.add(:age, "は10歳以上である必要があります") end end end
コンソールで挙動を確かめます。
>> Person.create(name: "John Doe", age:9).valid? => false >> Person.create(name: "John Doe", age:10).valid? TRANSACTION (0.2ms) begin transaction Person Create (1.2ms) INSERT INTO "people" ("name", "created_at", "updated_at", "age") VALUES (?, ?, ?, ?) [["name", "John Doe"], ["created_at", "2023-11-07 11:34:40.529798"], ["updated_at", "2023-11-07 11:34:40.529798"], ["age", 10]] TRANSACTION (1.5ms) commit transaction => true >> Person.create(name: "John Doe.exc", age:9).valid? TRANSACTION (0.2ms) begin transaction Person Create (1.2ms) INSERT INTO "people" ("name", "created_at", "updated_at", "age") VALUES (?, ?, ?, ?) [["name", "John Doe.exc"], ["created_at", "2023-11-07 11:34:53.336576"], ["updated_at", "2023-11-07 11:34:53.336576"], ["age", 9]] TRANSACTION (2.3ms) commit transaction => true
名前を「John Doe.exc」とすると、ageが9であってもエラーは生じず登録できる状態となることがわかりました。
シンプルな条件だったからでもありますが、思いの外短いコードでカスタマイズできました。