Ruby on Railsで外部キー設定

Ruby on Railsで外部キー設定

スムーズに行くかと思いきや、地味にいろいろやることがある。
ちょっとRailsを過信しすぎたかも。

作りたいテーブルのリレーションについて

アニメテーブルの1レコードに各話テーブルの複数レコードが紐付く

やったこと

各モデルクラスおよびテーブル作成
  • アニメモデル作成
$ rails generate scaffold anime title:string anime_image_url:string hash_tag:string
  • 各話モデル作成
$ rails generate scaffold chapter chapter:integer title:string start_time:timestamp end_time:timestamp tweet_collect_start_flg:boolean tweet_collect_end_flg:boolean anime:references
  • マイグレーションファイル確認
    モデル作成コマンドでは「anime:references」と指定しているが、作成されるテーブルのカラムとしては「anime_id」となるので注意。
$ view db/migrate/20130427141102_create_chapters.rb
class CreateChapters < ActiveRecord::Migration
  def change
    create_table :chapters do |t|
      t.integer :chapter
      t.string :title
      t.timestamp :start_time
      t.timestamp :end_time
      t.boolean :tweet_collect_start_flg
      t.boolean :tweet_collect_end_flg
      t.references :anime

      t.timestamps
    end
    add_index :chapters, :anime_id
  end
end
  • モデルクラス確認
    モデル作成コマンドでの「anime:references」により「belongs_to :anime」が自動追加されている。
$ view app/models/chapter.rb
class Chapter < ActiveRecord::Base
  belongs_to :anime
  attr_accessible :chapter, :end_time, :start_time, :title
end
$ rake db:migrate
  • 問題点
    • 特に問題無くテーブルは作成されたが、各話テーブルのanime_idに外部キー制約が付与されていない。
    • 調べたところ「references」はあくまでモデル上のリレーションを設定するもので、DBには反映されないとのこと。
      マイグレーションファイル内にSQLをベタで書くか、プラグインを使用するしか方法はない。今回はプラグインを使ってみた。
外部キー制約補助のためのGem「foreigner」を導入
  • インストール
$ vi Gemfile
gem 'foreigner'
$ sudo bundle install
$ vi db/migrate/20130427141102_create_chapters.rb
class CreateChapters < ActiveRecord::Migration
  def change
    create_table :chapters do |t|
      t.integer :chapter
      t.string :title
      t.timestamp :start_time
      t.timestamp :end_time
      t.boolean :tweet_collect_start_flg, null:false, :default => false
      t.boolean :tweet_collect_end_flg, null:false, :default => false
      # ついでに外部キーにNOT NULL制約を付与
      t.references :anime, null: false
      # 下記行を追加。
      # 「dependent: :delete」により親データが削除された際に子データも自動で削除させる。
      t.foreign_key :animes, dependent: :delete

      t.timestamps
    end
    add_index :chapters, :anime_id
  end
end
$ rake db:migrate:redo
  • 各話テーブルのanime_idに外部キー制約が設定されていることを確認。
その他の要修正ファイルについて
  • Modelクラスについて、scaffold時に「references」を使用したことにより特殊な項目と判断され「anime_id」が追加されてない。
    アプリ動作時に項目が見つからず正常に動作しないため項目を追加
$ vi app/models/chapter.rb
class Chapter < ActiveRecord::Base
  belongs_to :anime
  attr_accessible :chapter, :end_time, :start_time, :title, :anime_id
end
  • Viewのerbファイルについて、本来項目名が「anime_id」であるべきところ「anime」となっている。アプリ動作時に項目が見つからず正常に動作しないため修正
$ vi app/views/chapters/_form.html.erb
  <div class="field">
    <%= f.label :anime_id %><br />
    <%= f.text_field :anime_id %>
  </div>
<% end %>