3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

たったひとりの個人開発Advent Calendar 2022

Day 13

Railsのカスタムバリデーションとたたかう

Posted at

たたかう

 データの形式や数値について、制約を設けることをバリデーションという。Railsにはあるていど整ったバリデーションがあり、上手に使うことで簡単にいろいろできる。

 以下は私のアプリのReportモデルの記述だが、このようにcontentの文字数の最大値を設定したり、空白の場合はエラーとなるようにしている。

app/models/report.rb
# 略
  validates :content, length: { maximum: 200 }, presence: true

 ただ、細かいところを詰めようとすると、どうしても既存の表現ではうまくいかないところが出てくる。そうなったらつくるしかない。

 カスタムメソッドの登場だ。

カスタムメソッド

1. 過去の日付はNG

 例えば、「目標の締め切りは現在より過去の日付はNG」としたいなら、以下のように入力する。

app/models/plan.rb
class Plan < ApplicationRecord
# 略
  validates :deadline, presence: true
  validate :deadline_cannot_set_in_past

# 略

  def deadline_cannot_set_in_past
    if status == "進行中" && (deadline.present? && deadline < Date.today)
        errors.add(:deadline, ":ステータスが「進行中」の場合、過去の日付は使えません") 
    end
  end
end

validateにメソッド名を書いておき、紐づけるイメージだろうか。わりあいわかりやすい。

 Railsガイドにも同様の記述がある。

2. 条件を満たすデータの総数の制約

 次に、「進行中の目標の個数は3個まで」を設定した。これはアプリを使っていく上で、振り返りながら目標の状態を変更する流れがある。ただ、同時に進行中にできる目標は制限がないといけないので、制約を設けることにした。
 (振り返りの記述の際に、すこし面倒なのだ)

 それは以下のようになる。

app/models/plan.rb
class Plan < ApplicationRecord
 
# 略
  validate :progress_total_count_cannot_set_exceeded_limit
# 略
  PROGRESS_MAX_COUNT = 3
  def progress_total_count_cannot_set_exceeded_limit
    count_in_progress = user.plans.where(status: "進行中").count
    if count_in_progress > PROGRESS_MAX_COUNT
      errors.add(:status, ":「進行中」の目標の数は、3個までです") 
    end
  end
end

 ログイン中のユーザについて、進行中となっている目標の個数を出してくる。それだけ。オーバーならエラーが出るようにした。

3. 条件を満たすデータの変更禁止

 最後に、「完了となった目標はもういじっちゃだめ」という制約をつくった。これはすでに完了した目標は締め切りが過ぎているため、
「名前だけでも変更しよう...」
とすると、
「締め切りが現在より以前ですだめ!」
と怒られるためである。

 やっていく。

app/models/plan.rb
class Plan < ApplicationRecord
#略
  validate :cannot_edit_on_complete, on: :update
#略
  def cannot_edit_on_complete
    if status == "完了" && (deadline_changed? || name_changed?)
      errors.add(:deadline, ":完了状態で締め切りは変更できません") 
    end
  end
end

 on: :updateをつけることで、更新の時のみバリデーションが効くようにしている。また、_changed?をつけることで、属性が変更したかどうかを確認している。

テストもがんばっていくけど。。。

 バリデーションをつくったらテストも書かないといけないのだが、テストってぜんぜんわからん。これは来年やっていこう。

 

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?