概要
ActiveRecord
を継承したmodel
には、errors
で任意にエラーを追加できる- 追加したエラーは、
save
やvalidate
に成功すると、errors
から削除される
はじめに
ホワイトヘルスケア エンジニアの林です。
本記事は、ActiveRecord を使っていた際に、errors
に追加したエラーが意図せず消えてしまった時の挙動を調べた際の話になります。
発生した現象と調査について
事の始まり
1 対多の関係のテーブルに対し、ある機能を作成していた時の話です。
テーブルをざっくり例えると以下のようなイメージのものでした。
user
が複数のarticles
を所有しているarticles
は、公開/非公開のstatus
、記事のtext
、記事の文字数text_count
をフィールドとして持つuser
は、公開記事の全文字列の合計数であるpublished_text_count
をフィールドとして持つ
図として表現するとこのような感じ。
erDiagram users ||--|{ articles: "" users { integer published_text_count } articles { enum status string text integer text_count }
コードで model を表現するとこのような感じ。
class User < ApplicationRecord has_many :articles end class Article < ApplicationRecord belongs_to :store_invoice enum :status, { DRAFT: 'draft', # 下書き PUBLISH: 'publish', # 公開 } end
発生した問題
作成していた機能は、以下のようなものでした。
user
を指定し、articles
を更新するarticle
がDRAFT
だった場合、PUBLISH
に変更するarticle
がPUBLISH
だった場合、変更できなかったArticle.id
のリストをエラーとして表示する
article
がPUBLISH
であるtext
の合計値であるpublished_text_count
を更新する
上記をdraft_to_publish
として以下のように実装しました。
class User < ApplicationRecord has_many :articles def draft_to_publish # 元々PUBLISHだったものは、idをエラーとして保持 errors.add(:base, articles.PUBLISH.ids) # DRAFTはPUBLISHに変更 articles.DRAFT.update(status: :PUBLISH) # `published_text_count`の更新 published_text_count = articles.PUBLISH.sum(:text_count) # フィールド値の更新 save end end # 想定していた使い方 # 対象のユーザでmethodをcall user = User.first user.draft_to_publish # PUBLISHのarticlesがあれば、このraiseでcall側に通知することを期待してた raise StandardError(user.errors.full_messages) if user.errors.empty?
しかし、PUBLISH
のarticles
が存在するuser
であっても、エラーがraise
されることがなかったため、調査することにしました。
原因の調査
結論から言うと、save
(正確にはvalid?
)でerrors
がクリアされている、ということがわかりました。
該当箇所:https://github.com/rails/rails/blob/v7.1.2/activemodel/lib/active_model/validations.rb#L363-L369
明示された文章を探し出せなかったのですが、rails の思想としては、「あるタイミングでエラーが発生しても、その後の処理で保存できる(検証 OK になった)なら、値の修正などでエラーが解消されたとみなす」ということなのだろうと理解しました。
まとめ / 感想
一般的な(?)使い方を考えると、「新規に validation に成功したら errors
がクリアされる、は確かにそうだな〜」と思いました。が、反面、自分の認識していないところでデータが書き換わるのは想定しておらずだいぶ困惑しました。
Framework を使う場合、どういった使い方を想定されて作られているのか、という点を意識して使う必要があることを再認識させられる出来事でした。
採用情報
Ruby on Railsでアプリ開発をしているホワイトヘルスケアが気になった方は、ぜひお気軽にお問い合わせください!