子テーブルのレコードにカスタムバリデーションで存在検証
したかったこと
親レコードと同時にネストしたフォーム(fields_for)で作成した子レコードに最低一つは作成されていることを判定をするカスタムバリテーションを作成した。(質問を作成した時に最低でも一つはジャンルを選択してほしい)
なぜカスタムバリデーションなのか?
validates :tag, presence: trueで子レコードの存在を確認しようとしましたが、questionモデルでバリテーションの処理時にtagがundefined methodとなってしまうためカスタムしようとしました。
追記
(実はカラム名ではなく、小テーブル名でバリテーションをすると、存在を検証できるようです。binding.pryでデータを追っていたらこれでエラーが追加されることを確認できました。validates :questiontags, presence: true)
テーブルの関係図
フォーム
questionsのレコードに対して、questionsTagsのレコードは複数作成される。
form.html.haml
学校、仕事などのタグの分だけfields_forでインスタンスが生成されている。
ちなみにtagはモデルでenumで定義済み。
= form_for @question do |f| .form-group %h5.main-color-bottom-border タイトル =question_error_messages(@question,"title") =f.text_field :title, class: "form-control ",placeholder: "タイトル" .form-group.mt-2 %h5.main-color-bottom-border 質問のジャンルを選択 =question_error_messages(@question,"base") - QuestionTag.tags_i18n.map do |k, v| - @genre = [] if @genre.nil? = f.fields_for :questionTags do |qt| - break form(qt,k,v,@genre) .form-group %h5.main-color-bottom-border 相談内容 =question_error_messages(@question,"body") = f.text_area :body, class: 'form-control', placeholder: '相談内容',rows: 15 = f.submit '相談する', class: 'btn btn-success mb-3'
実装したカスタムバリテーション
questionTagモデルではなくネストしている親モデルのquestionに定義します。
ここで生成されたインスタンスのerrorにno_questionTagが追加されて、それをviewで表示することができます。
class Question < ApplicationRecord has_many :questionTags, dependent: :destroy accepts_nested_attributes_for :questionTags validate :require_any_questionTags #カスタムバリテーション def require_any_questionTags errors.add(:base, :no_questionTag) if questionTags.blank? #questionTagsが一つもなければ、エラーを追加 end end
エラー表示
これで実際にジャンルを選択しないで、日本語のエラーを表示するとこのようになります。