アラサーからのエンジニア転身blog

プログラミングの勉強や転職に関して記事にしていきます。

子テーブルのレコードにカスタムバリデーションで存在検証

f:id:sakurai-chan:20181021003040p:plain

したかったこと

親レコードと同時にネストしたフォーム(fields_for)で作成した子レコードに最低一つは作成されていることを判定をするカスタムバリテーションを作成した。(質問を作成した時に最低でも一つはジャンルを選択してほしい)

なぜカスタムバリデーションなのか?

validates :tag, presence: trueで子レコードの存在を確認しようとしましたが、questionモデルでバリテーションの処理時にtagがundefined methodとなってしまうためカスタムしようとしました。

追記
(実はカラム名ではなく、小テーブル名でバリテーションをすると、存在を検証できるようです。binding.pryでデータを追っていたらこれでエラーが追加されることを確認できました。validates :questiontags, presence: true


テーブルの関係図

f:id:sakurai-chan:20181009195218p:plainquestionsとquestionTagsの関係

フォーム

questionsのレコードに対して、questionsTagsのレコードは複数作成される。
f:id:sakurai-chan:20181021003040p:plain

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

エラー表示

これで実際にジャンルを選択しないで、日本語のエラーを表示するとこのようになります。

f:id:sakurai-chan:20181021005539p:plain
エラー表示