RailsでSTI(単一テーブル継承)の予約語でハマった話対処

はじめに


ここ数日、自分用のアプリを作っているわけなのですが
その時に起きたエラーを記載しておきます

(STIの予約語のこと忘れてたというオチw)

事象


ActiveRecordで「type」というカラムをもつテーブルに接続し、取得系のメソッドを呼ぶと以下の様なエラーが出た

1
2
3
[1] pry(main)> Hoge.find(1)
  Hoge Load (0.4ms)  SELECT `hoges`.* FROM `hoges` WHERE `hoges`.`id` = 1 LIMIT 1
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'fuga'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Hoge.inheritance_column to use another column for that information.

エラーメッセージから分かるように「type」というカラムをrenameしろと出ています
こんなの場合によっては出来無いわけで・・・

これは何?


STI(単一テーブル継承)と呼ばれる機能のことを指しています
つまり、1つのテーブルを複数のModelで利用する仕組みのことを言います。

で、その仕掛けように予約語として、”type”という名前をActiveRecordが利用しているようです。

クラスで言うとこんなところでしょうか。
Humanクラスを継承した、Manクラス、Womanクラスがあったとした場合、以下のような形になります

1
2
3
4
5
6
7
8
9
10
11
class Human < ActiveRecord::Base

end

class Man < Human

end

class Woman < Human

end

で、例えばManオブジェクトをsaveすると、Humanテーブルにtype:Manとして保存されます。
Womanならtype:Womanになるようです

それぞれのモデルからデータへの操作を行った場合、適合するTypeのデータのみを操作対象となります
ManもWomanも関係なく取得したい(両方をまとめて扱いたい)場合は、Humanモデルから操作すればOKです

当然ひとつのテーブルを使っていますので、片方にしか無い項目(カラム)もテーブルに含まなければならないです。
例えばManとHumanの持つ項目があまりにも違う場合、カラム数が爆発的に増えます。
(ここは注意するところですね)

対処


もうざっくりというと、以下の2つの対応ができます

  1. カラム名に「type」という名前をつけず、別名に変更する
  2. エラーメッセージにあるようにSTIのカラム名を変更する

というわけなので、自分は1の対応を行いました。

では、2の場合はどうするかというと・・・ エラーメッセージにありますが、以下ように設定すればよいようです

1
2
3
class Hoge < ActiveRecord::Base
  self.inheritance_column = :_type_disabled
end

どういう場合に起きそう


ざっと思いつくのはこんなところかな

  • 既存のアプリケーション(PHPなりJavaなり)をRailsにのせかえるとき
  • 設計上どうしても止むえずつけたいとき(これはビジネスドメインの場合が当てはまる)
  • (あんまりないと思うが)STIの機能を使ってリファクタリングするとき