子モデルで親モデルの関連データがバリデーションができない問題


作ろうとした機能は、タグに応じて投稿フォームの種類を切り替えるやつ。

モデルの関係はこんな感じ(子モデルの名前は適当です)

タグに応じて子モデルが変わり、投稿フォームも変わるようになっています。
しかしこのモデル設計だと、タグの種類によってバリデーションが変えられない問題が生じました。

例えば、子モデルでバリデーションをする場合

One.rb
class One < ApplicationRecord
    belongs_to :post

    with_options if: :one? do |one|
        one.validates :url, presence: true
    end

   def one?
        tag_id == 1
    end
end

このようなエラーがでます。

undefined local variable or method `tag_id' 

tag_idが見つからないと怒られてしまいました。
いろいろ調べたところ、上のコードが機能するようにするためには、子モデルが親モデルを継承する必要があるそうです。

しかし、機能追加・機能変更のための継承はよくないとのこと。
参考:https://qiita.com/tonluqclml/items/c0110098722763caa556

そこで、Postモデルを消す形でモデルを修正しました。
Postモデルの子モデルは全て並列の関係になっていたので、モデル設計的にも正しいと思います。

モデルを書き換えない方法

とはいえ、モデルを書き換えるのは大変なので、緊急的に動作するコードも書いてみました。
結論から言うとカスタムバリデーションを用いると実装できます。
ただ、バリデーションが増えると重複コードが増えて汚くなってしまいます。

app/validtors/three_valiator.rb
class ThreeValidator < ActiveModel::EachValidator
    def validate(record)      
    #先生の紹介のバリデーション
      if record.tag_id == 4
        unless record.teacher.name.present?
            record.errors[:base] << "名前を入力してください"
        end

        unless record.teacher.job.present?
            record.errors[:base] << "職業を入力してください"
        end
      end
    end
  end

もっと良い方法とかあれば、教えてください。

参考文献

https://railsguides.jp/active_record_validations.html
https://qiita.com/tonluqclml/items/c0110098722763caa556
https://qiita.com/kikunantoka/items/61ab74c7f50dedace211