LoginSignup
0
0

[Rails]STIをいじくり回してみる[Single Table Inheritance][単一テーブル継承]

Posted at

STIのモデルのデータ移行・リファクタリングみたいなことをした際に得られたSTIをいじくり回す知見を書き留めておく

環境

ruby 3.2.2
Rails 7.0.8

STIに関するコードはこの辺

前提

class Person < ApplicationRecord
  self.store_full_sti_class = false
end

class Person
  class Child < Person
  end
end

みたいなモデルがあるとする

クラス名とsti_nameを別のものにしたい時は

class Person < ApplicationRecord
  self.store_full_sti_class = false

  class << self
    def find_sti_class(type_name)
      case type_name
      when 'Baby'
        Person::Child
      else
        super(type_name)
      end
    end
  end
end

class Person
  class Child < Person
    class << self
      def sti_name
        'Baby'
      end
    end
  end
end

こうすることでtypeBabyのレコードをPerson::Childとして扱うことができる

解説

sti_nameをオーバーライドするだけだとDBのレコードを取得してインスタンス化するときに「Babyに対応するクラスPerson::Babyがない」というエラーが出るのでfind_sti_classをオーバーライドしてBabyPerson::Childのマッピングをする必要がある

レコード検索時に発行されるSQLのWHERE ***.type = '****'を任意の値に変えたい時は

class Person < ApplicationRecord
  self.store_full_sti_class = false

  class << self
    def find_sti_class(type_name)
      case type_name
      when 'Baby'
        Person::Child
      else
        super(type_name)
      end
    end
  end
end

class Person
  class Child < Person
    class << self
      def type_condition(table = arel_table)
        sti_column = table[inheritance_column]
        sti_names  = ['Baby', 'Child']

        predicate_builder.build(sti_column, sti_names)
      end
    end
  end
end

こうすることでtypeBabyのレコードとChildのレコードの両方をPerson::Childとして扱うことができる

解説

type_conditionというメソッドがレコード検索時のtypeの絞り込みをしているのでそこをオーバーライドすることでWHERE type IN ('Baby', 'Child')というSQLになる

ただし、それだけだとDBのレコードを取得してインスタンス化するときに「Babyに対応するクラスPerson::Babyがない」というエラーが出るので併せてfind_sti_classをオーバーライドしてBabyPerson::Childのマッピングをする必要がある

ちなみに

class Person
  class Child < Person
    default_scope { unscope(where: :type).where(type: %i[Baby Child]) }
  end
end

ってやるとぱっと見動くけどjoinした時や、アソシエーションでの参照時のクエリが意図した挙動にならなかったです

RailsのコードをオーバーライドするというパワープレイはRailsのバージョンを上げる際の妨げになりやすいので一時的な対応としてのみ使いましょう

参考

type_conditionのコード
find_sti_classのコード

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0