railsでSTIの使い方
自分のサイトを作ろうとしていて、STI(Single Table Inheritance: 単一継承テーブル)を使ってみました。
STIは一つのテーブルを継承したクラスを作成することができます。
- modelにtype(string)を入れる
- modelから継承したクラスを作成する。
簡単な図で表すとこんな感じです。
継承しているのでどのモデルでも、カラムは全部使えます。
状況
「アニメ」「スポーツ」など、カテゴリーによる発言者を分けようとしとしました。
- アニメ→キャラクター
- スポーツ→選手
こういう状況で何個もモデル(キャラクター・選手)を作るのがめんどくさかったので、STIを行いました。
使い方
発言者をSpeakerモデルとします。
bundle exec rails g model Speaker name:string type:string
app/db/migrate/xxxx..._create_speakers.rb
class CreateSpeakers < ActiveRecord::Migration def change create_table :speakers do |t| t.string :name, null: false ←validationを先に追加(好みで) t.string :type t.timestamps null: false end end end
bundle exec rails g model player --parent speaker
app/models/player.rb
class Player < Speaker has_many :sports end
bundle exec rails g migration Sport body:string
app/db/migrate/xxxx..._create_speakers.rb
class CreateSports < ActiveRecord::Migration def change create_table :sports do |t| t.text :body, null: false ←validationを先に追加(好みで) t.timestamps null: false end end end
bundle exec rails g model Sport body:string
app/db/migrate/xxxx..._create_speakers.rb
class CreateSports < ActiveRecord::Migration def change create_table :sports do |t| t.text :body, null: false ←validationを先に追加(好みで) t.timestamps null: false end end end
ID追加
bundle exec rails g migration AddColumnToSport player_id:integer
app/db/migrate/xxxx..._add_column_to_sport.rb
class AddColumnToSport < ActiveRecord::Migration def change add_column :sports, :player_id, :integer add_index :sports, :player_id←indexを先に追加(好みで) end end
これでPlayerモデルとSportモデルの関連づけができました。
irb(main):001:0> Player.create(name: 'ダルビッシュ') => #<Player id: 1, name: "ダルビッシュ", type: "Player", created_at: "2016-06-05 06:15:39", updated_at: "2016-06-05 06:15:39"> irb(main):002:0> Sport.create(body: '健康大事', player_id: 1) => #<Sport id: 1, body: "健康大事", created_at: "2016-06-05 06:16:07", updated_at: "2016-06-05 06:16:07", player_id: 1> irb(main):003:0> player = Player.find(1) => #<Player id: 1, name: "ダルビッシュ", type: "Player", created_at: "2016-06-05 06:15:39", updated_at: "2016-06-05 06:15:39"> irb(main):004:0> player.sports => #<ActiveRecord::Associations::CollectionProxy [#<Sport id: 1, body: "健康大事", created_at: "2016-06-05 06:16:07", updated_at: "2016-06-05 06:16:07", player_id: 1>]>
自分的にはあまりクラス独自のカラムを作りたくないので、そういう場合は、新しくtableを作って分岐さすのがいいのかもしれません。
以上、すごく簡単な使い方になります。