
今日はバリデーションをカスタマイズできることを知りました。
正直、指摘いただいた後に、何故気づかなかったのか不思議でした。
欲しいメソッド・機能はすでに先人が作ってくれていることがほとんどです。
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であってもエラーは生じず登録できる状態となることがわかりました。
シンプルな条件だったからでもありますが、思いの外短いコードでカスタマイズできました。