前提・実現したいこと
RailsでSTIを用いた開発を行っています。
そこで、子クラスのロードがされない問題が発生したため https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#single-table-inheritance を参考に、STI子クラスの自動ロードをするコードを書きました。
発生している問題・エラーメッセージ
rails consoleにて次のコードを実行する
>> user = User.create!(name: "Hoge") => User id=1 >> tweet = user.normal_tweets.create!(text: "Fuga") => NormalTweet id=1 >> tweet.author => User id=1
rails consoleを再起動し、次のコードを実行する
>> Tweet.find(1).author => C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-7.0.1/lib/active_record/associations.rb:301:in `association': Association named 'author' was not found on NormalTweet; perhaps you misspelled it? (ActiveRecord::AssociationNotFoundError)
このように、エラーが発生します。※Tweetモデルからinclude StiPreloadを削除した場合この問題は起きません。
>> Tweet.find(1).author_id => 1
もちろん、外部キーであるauthor_id自体は存在しています。
該当のソースコード
rb:user.rb
1class User < ApplicationRecord 2 has_many :normal_tweets, dependent: :destroy, class_name: "NormalTweet", foreign_key: :author_id 3end
rb:tweet.rb
1require "sti_preload" 2 3class Tweet < ApplicationRecord 4 include StiPreload 5 6 belongs_to :author, class_name: "User" 7end
rb:normal_tweet.rb
1class NormalTweet < Tweet 2 has_many :replies, class_name: "Reply", foreign_key: :reply_to_id, dependent: :nullify 3end
rb:
1class Reply < NormalTweet 2 belongs_to :reply_to, class_name: "NormalTweet", optional: true 3end
rb:sti_preload.rb
1module StiPreload 2 unless Rails.application.config.eager_load 3 extend ActiveSupport::Concern 4 5 included do 6 cattr_accessor :preloaded, instance_accessor: false 7 cattr_accessor :preloading, instance_accessor: false 8 end 9 10 class_methods do 11 def descendants 12 preload_sti unless preloaded 13 super 14 end 15 16 def preload_sti 17 return if self.preloading 18 self.preloading = true 19 20 types_in_db = \ 21 base_class. 22 unscoped. 23 select(inheritance_column). 24 distinct. 25 pluck(inheritance_column). 26 compact 27 28 types_in_db.each do |type| 29 logger.debug("Preloading STI type #{type}") 30 type.safe_constantize # 重要: safeでないconstantizeを使うと例外が発生する 31 end 32 33 self.preloading = false 34 self.preloaded = true 35 end 36 end 37 end 38end
https://github.com/PJZ9n/RailsSTIProblem
試したこと
StiPreloadモジュールを使わず、手動でロードしてやると問題なく動作します。
>> [Tweet, NormalTweet, Reply]
補足情報(FW/ツールのバージョンなど)
$ rails -v Rails 7.0.1 $ ruby -v ruby 3.0.3p157 (2021-11-24 revision 3fb7d2cadc) [x64-mingw32]
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。