Rails 7: 数値バリデータにonly_numericオプションが追加(翻訳)
Active Recordには、クラス定義内で直接利用できる多くのバリデーションヘルパーが事前定義されています。これらのヘルパーが提供するバリデーションルールは共通しています。つまり、バリデーションが失敗すればオブジェクトのエラーコレクションにエラーが追加され、このエラーはバリデーションが行われる属性に関連しています。
また、コードを1行書けば、さまざまな属性に同じバリデーションを追加できます。これが可能なのは、どのバリデーションヘルパーにも任意の個数の属性名を渡せるからです。
さまざまなヘルパーの中にはnumericalityヘルパーもあります。このヘルパーは、属性が数値であることを保証します。
numericalityヘルパーの動作を理解するために、最初にJSONカラムを持つテーブルを生成します。
create_table :cricket do |t|
t.jsonb :points
end
変更前のバリデーション
従来のRailsでは、numericalityバリデータに整数ですらでない値を渡してもエラーにならずにパスしていました。以下の例を見ていただければおわかりかと思います。
class Cricketer < ApplicationRecord
store_accessor :points, %i[scores]
end
>> Cricketer.create!(scores: "30")
#<Cricketer id: 1, points: {"scores" => "30"}, created_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC +00:00, updated_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC >
訳注
上のコード例はたとえば以下のようにonly_integer
を指定するつもりだったと思われます。
class Cricketer < ApplicationRecord
store_accessor :points, %i[scores]
validates_numericality_of :scores, only_integer: true, allow_nil: true
end
Rails 7.0.2.2 + Ruby 3.1.1で試すと、たとえば"a"
はValidation failed: Scores is not a number (ActiveRecord::RecordInvalid)
エラーになりますが、"30"
という文字列はたしかに記事にあるようにパスしてしまいました。
変更後のバリデーション
先ごろRails 7.0にonly_numeric
オプションが導入され、属性の値に数値のみを許可できるようになりました(#43914)。つまり、数字の文字列を値として渡すと、ちゃんと解析してエラーを発生するようになります。
以下の例を見ていただければおわかりかと思います。
class Cricketer < ApplicationRecord
store_accessor :points, %i[scores]
validates_numericality_of :scores, only_numeric: true, allow_nil: true
end
>> Cricketer.create!(scores: "30")
'raise_validation_error': Validation failed: Scores is not a number (ActiveRecord::RecordInvalid)
>> Cricketer.create!(scores: 30)
#<Cricketer id: 1, points: {"scores" => 30}, created_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC +00:00, updated_at: Sun, 04 Feb 2022 14:09:43.045301000 UTC >
通常、JSONカラムのデータは自動的にはシリアライズされないので、データを正しくシリアライズできるよう、このオプションがRailsのnumericalityバリデータに追加されました。
詳しくは#43914をご覧ください。
訳注
Railsのmainブランチ(74ba52e)でonly_numeric
を使うことで上記の動作を確認できました。
なお、従来からあるonly_integer
の挙動は変わっていません。整数は数値の30
でも文字列の"30"
でもパスし、30
なら整数で、"30"
なら文字列で登録されます。
"a"
などの非数値文字列や小数3.14
や小数の文字列"3.14"
は以下のエラーになります。
Validation failed: Scores must be an integer (ActiveRecord::RecordInvalid)
概要
元サイトの許諾を得て翻訳・公開いたします。
なお、この改修はRails 7.0.0〜7.0.2には入っていません。現時点ではmainブランチに入っています。