マイグレーションで外部キーを設定する2通りの方法

マイグレーション、やればやるほど便利ですね。
単純に、テーブル操作だけじゃなくて、ファイル保存先の変更とか、内部仕様の変更に対しても使えそうな印象を受けます。

基本的なやり方・書き方は、巷にあふれかえっていたので、そこは調べていただくとして。
今回は、特に、マイグレーションでの外部キーの設定の仕方について、メモ。

マイグレーション、基礎

そもそも、マイグレーションってどうやるの?って人は、以下のところを参照されるとわかりやすいかと思います。

1 : SQLを利用する場合

ひとつめのやり方として、外部キーの設定のところだけ、SQLで呼び出すやり方があります。
たとえば、こんな感じ。

RUBY:
  1. def self.up
  2.     options = {
  3.       :force => true,
  4.       :options => "ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
  5.     }
  6.     create_table(:images, options) do |t|
  7.       t.column(:user_id, :integer, :null => false)
  8.       t.column(:image, :string, :null => false)
  9.       t.column(:comment, :text)
  10.       t.column(:created_at, :timestamp)
  11.       t.column(:updated_at, :timestamp)
  12.     end
  13.     execute "alter table images add constraint fk_images_users foreign key (user_id) references users(id)"
  14.   end

ユーザーがアップロードした画像を管理するテーブルのイメージでお願いします。
各ユーザーに画像のデータがぶら下がってる感じです。

とはいえ、こんなんじゃ、外部キーを設定しているのはわかるけど、いまいちわかりにくいですね。
それに、外部キーを設定するのはこれ一個とは限らないので、外部キーの設定のところだけ汎用化してしまいたいところです。

1' : SQLを利用する場合・改

汎用化したいので、libフォルダ直下に、新しくヘルパー用のクラスを作ってしまいます。
参照元の丸パクリで恐縮ですが、MigrationHelperというクラス名で作ってみましょう。

RUBY:
  1. module MigrationHelper
  2.  
  3.   # 外部キー作成メソッド.
  4.   # migrationで外部キーを実行する場合のSQLを見えないようにして、migrationファイルの可視性アップを狙う.
  5.   def foreign_key(from_table, from_column, to_table)
  6.     constraint_name = "fk_#{from_table}_#{to_table}"
  7.    
  8.     execute "alter table #{from_table} add constraint #{constraint_name} foreign key (#{from_column}) references #{to_table}(id)"
  9.   end
  10.  
  11. end

で、さっきのマイグレーション用のクラスのソースをこう直します。

RUBY:
  1. require 'migration_helper'
  2. class CreateImages <ActiveRecord::Migration
  3.  
  4.   # 外部キー用のモジュールを使えるように拡張する.
  5.   extend MigrationHelper
  6.  
  7.   # imagesテーブルを作成する.
  8.   def self.up
  9.     options = {
  10.       :force => true,
  11.       :options => "ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
  12.     }
  13.     create_table(:images, options) do |t|
  14.       t.column(:user_id, :integer, :null => false)
  15.       t.column(:image, :string, :null => false)
  16.       t.column(:comment, :text)
  17.       t.column(:created_at, :timestamp)
  18.       t.column(:updated_at, :timestamp)
  19.     end
  20.     foreign_key(:images, :place_id, :places)
  21.   end
  22.  
  23.   # imagesテーブルを削除する.
  24.   def self.down
  25.     drop_table :images
  26.   end
  27. end

注意するのは、requireとextendのところを忘れないこと。
でないと、NoMethodErrorとかになります。

これで、見た目直感的で、保守がしやすそうなソースになりましたね。

2 : プラグインを利用する場合

中には、それプラグインで、という人もいらっしゃるかと思われますので、ふたつめのやり方としてそちらをばご紹介。

結論から言えば、Foreign Key Migrationsというプラグインがございます。
これは、詳しくはリンク先のページ(英語)もしくは、日本語訳されてるこちらを当たってもらえればと思いますが、railsの規約に沿っていれば、外部キーの設定は書かなくても、勝手に外部キーの設定もするよ、というステキプラグインです。

なので、さっきのソースで行くと、これだけで同じことができてしまいます。

RUBY:
  1. def self.up
  2.     options = {
  3.       :force => true,
  4.       :options => "ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;"
  5.     }
  6.     create_table(:images, options) do |t|
  7.       t.column(:user_id, :integer, :null => false)
  8.       t.column(:image, :string, :null => false)
  9.       t.column(:comment, :text)
  10.       t.column(:created_at, :timestamp)
  11.       t.column(:updated_at, :timestamp)
  12.     end
  13.   end

ちなみに、このプラグイン。
Foreign Key MigrationsRedHill on Rails Coreという二つのプラグインをインストールしないと動きませんので、インストールする場合はご注意ください。

また、使ってみてわかりましたが、テーブルをdropしようとすると、エラーになってうまく消せないみたいです。
(※ただし、MySQL Administratorとかのアプリからdropする分には、何も問題がありません)
原因を深く追いかけなかったのでアレなんですけど、スキーマをダウングレードするのもちゃんとできなきゃイヤ! って人には向かないかと思われます。。

個人的には

1'のやり方が一番しっくり来ますね。
何でもかんでもプラグイン頼みしてしまうのは、それだけブラックボックスなソースを受け入れることでもあるし、考え物なのかもな、と、この一件で思ってしまったので。。。

Via:

このエントリをはてなブックマークに追加このエントリをdel.icio.usに追加 |

0 Responses to “マイグレーションで外部キーを設定する2通りの方法”


  1. No Comments

Leave a Reply