Rails で既存テーブルに STI を適用する時に気をつけること
Rails の STI (Single Table Inheritance) は、1つのテーブルで複数のモデルを管理できる便利な機能です。しかし、既存のテーブルを STI に移行する際には注意が必要です。今回は、実際に遭遇した問題とその解決策について共有します。
問題が発生した状況
既存のテーブルに STI を導入するため、以下の変更を1回のリリースで行おうとしました。
- type カラムの追加
- サブクラスの追加
デプロイの流れは以下のようになっていました
- データベースのマイグレーション実行(db:migrate)
- Rails アプリケーションの再起動
この時、マイグレーション実行から Rails の再起動までの間に ActiveRecord::SubclassNotFound
エラーが発生しました。
なぜ問題が起きたのか
Rails では、テーブルに type カラムが存在すると、自動的に STI として扱われます。つまり、type カラムを追加した時点で、Rails はそのカラムに対応するサブクラスを探しにいきます。しかし、サブクラスの定義がまだデプロイされていない状態だったため、エラーが発生してしまいました。
どうすればよかったか
この問題を避けるためには、リリースを2段階に分ける必要がありました。
Step 1: サブクラスの追加と inheritance_column の無効化
最初のリリースでは
- サブクラスを追加する
-
inheritance_column
を nil に設定し、STI を一時的に無効化する
Step 2: STI の有効化
次のリリースで
- データベースのマイグレーションを実行
-
inheritance_column
の設定を削除し、STI を有効化する
この設定方法はRailsガイドにバッチリ記載がありました
https://railsguides.jp/association_basics.html#継承カラムを無効にする
学び
この経験から学んだ重要な点は
- 機能テストだけでなく、デプロイプロセス自体も考慮に入れる必要がある
- データベースの変更を伴う機能追加は、段階的なリリースを検討する
- Rails の STI の仕組みについて、より深い理解が必要
今回は幸いにも影響が小さかったものの、より重要なテーブルであれば大きな問題になっていた可能性があります。既存のテーブルでSTIを使いたいケースはそう多くないとは思いますが、似たような状況に遭遇した方の参考になれば幸いです。
「物流の次を発明する」をミッションに物流のシェアリングプラットフォームを運営する、ハコベル株式会社 開発チームのテックブログです! 【エンジニア積極採用中】t.hacobell.com/blog/career
Discussion