LoginSignup
0
0

More than 1 year has passed since last update.

【Rails】find_or_create_byでバリデーションをskipさせたい

Last updated at Posted at 2022-05-21

結論

1.saveに validate: falseを渡す

  • validate: falseを末尾に記載することで、バリデーションをskipできました。
  • 加えて、無意味なsaveを2回走らせないように、find_or_create_by から find_or_initialize_by に修正しました。
Model.find_or_initialize_by(name: "テスト") do |m|
  m.published_at = Time.zone.today - 1 # バリデーション対象のコード
  m.save(validate: false)  #=> バリデーションをskipさせる
end

補足

@jnchitoさんよりコメントいただきました。

1.new_record?メソッドを使用する

ブロックの外でsave(validate: false)を実行します。

# この時点ではsaveしない
model = Model.find_or_initialize_by(name: "テスト") do |m|
  m.published_at = Time.zone.today - 1
end
# ここで初めてsaveする
model.save(validate: false) if model.new_record?

2.バリデーションを通さずに、カラムを直接更新する

update_columnsを使用することで、バリデーションを通さないで更新することができます。

# !を付けて意図しないバリデーションエラーが発生したら例外を発生させる
model = Model.find_or_create_by!(name: "テスト")

if model.published_at.nil?
  # Railsのバリデーションを通さず、DB上のpublished_atカラムを直接更新する
  model.update_columns(published_at: Time.zone.today - 1)
end

背景

目的
作成日よりも遅い日付のテストデータを作成したい

現状
「作成日よりも遅い日付は登録できない」というバリデーションを設定しているため、保存できない

対応策
テストデータを作成するときのみ、バリデーションをskipさせたい

改善前

find_or_create_byでテストデータを作成していたが、バリデーション設定後、エラーするようになった。

# エラーになるコード
Model.find_or_create_by(name: "テスト") do |m|
  m.published_at = Time.zone.today - 1  # 作成日よりも遅い日付を渡すと、エラーになる。
end

調査と結果

  • saveメソッドにオプションを渡すことで、skipできることがわかった。
save(validate: false)
  • それをブロックの最後で実行することで、skipさせることができた!
Model.find_or_create_by(name: "テスト") do |m|
    
  # 略

  m.save(validate: false)  #=> 追加
end

参考資料

0
0
2

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
0
0