1つのカラムにvalidatesとvalidateをつけたときに気をつけたいこと

仕事中に引っかかりそうになったので、備忘録がてら書きたいと思います。

 

RailsにおけるValidatesとValidateの違い

Validates

validatesは特定のカラムに対して、予めRailsが用意している条件で

バリデーションをかけたい時に使うメソッドです。

例えば下記では、nameカラムに必須条件を加えています。

他にも一意条件である、uniquenessや値の長さをチェックするlengthなどがあります。

```

class Person < ApplicationRecord

  validates :name, presence: true

end

```

 

Validate(カスタムメソッド)

カスタムメソッドとは、ユーザーが独自に作成出来る、モデルの状態を確認し不正な状態の時にerrorsコレクションにメッセージを追加することが出来る機能です。

例えば下記の様なMemberモデルがあるとします。

Memberモデルは入会日(start_date)と退会日(end_date)を持つとした時、

入会日は常に退会日より以前であるということをバリデーションの条件として

追加したい時は下記の様に設定できます。

```

class Member < ApplicationRecord

  validate :check_start_date_before_end_date

 

  def check_start_date_before_end_date

    return if start_date < end_date

 

    errors.add(:start_date, ": 入会日は退会日より後にはできません")

  end

```

 

validatesメソッドもvalidateメソッドもRailsの開発では必須なので、使う機会は頻繁にあるので、ちゃんと理解しておかないといけなかったのですが、今回は下記の様なパターンでちょっと引っかかってしまいました。

 

同一カラムに対してvalidatesとvalidteを使う

ずばり下記の様な条件で引っかかりました。

```

class Member < ApplicationRecord

  validates :start_date, presence: true

  validates :end_date, presence: true

  validate :check_start_date_before_end_date

 

  def check_start_date_before_end_date

    return if start_date < end_date

 

    errors.add(:start_date, ": 入会日は退会日より後にはできません")

  end

```

 

start_dateにもend_dateにも必須制約をつけていたので、すっかり油断していました。

これ、check_start_date_before_end_dateを通る時に、

start_dateもしくはend_dateがnilだった時に、

`NoMethodError: undefined method start_date for nil:NilClass`

で落ちてしまうんですね。

 

バリデーションはそれぞれ独立している

バリデーションでは、どこかで制約に引っかかった、引っかかっていない関係なしに、

設定しているバリデーションを全て実行する訳ですね。

そして、制約に引っかかったものは全てerrorsに追加され、

最終的にerrors.empty?がtrueならvalidはtrue

1つ以上でもエラーメッセージが追加されており、errors.empty?がfalseなら

validはfalseになるわけです。

 

ついつい、validatesで必須制約は作ったから

validateではチェックしなくていいと安心しがちですが、気をつけるべきでした。