ActiveRecord で id 以外のカラムをプライマリキーにして、しかも型が int じゃないときの話
http://www.hsbt.org/diary/20120524.html#p01 見てそういえばハマった話思い出したので、次みたときなんでこうしたんだっけ、とならないように書いておく。
既に存在するDBをそのまま使う必要があったりする時で、しかもプライマリキーが int(11) とかじゃないときは、少し残念な気持ちになりながら以下のようなマイグレーションファイルを用意することになる。
class CreatePrefMaster < ActiveRecord::Migration def change create_table :pref_master, id: false do |t| t.column :code, :"char(2)", null: false t.column :name, :"varchar(64)" end execute "ALTER TABLE pref_master ADD PRIMARY KEY (code)" end end
テーブル名が複数形でアンダースコア繋ぎという Rails の標準的な命名規則ではない場合もあるので、モデルのほうで table_name を指定する。
class PrefMaster < ActiveRecord::Base self.table_name = "pref_master" attr_protected :title, :body end
さらに、config/application.rb で、db/schema.rb じゃなく db/structure.sql を吐くよう指定する。
module Aaa class Application < Rails::Application # Use SQL instead of Active Record's schema dumper when creating the database. # This is necessary if your schema can't be completely dumped by the schema dumper, # like if you have constraints or database-specific column types config.active_record.schema_format = :sql # ↑ここのコメントアウトはずす。 # ... end end
なぜかと言うと、上記の手順でマイグレーションして schema.rb を吐いたりすると以下のようなファイルができる。create_table オプションに直接 :primary_key オプションを指定しているため、Rails 的な primary_key の解釈で int な型のカラムで create table してしまう。
ActiveRecord::Schema.define(:version => 20120531162643) do create_table "pref_master", :primary_key => "code", :force => true do |t| t.string "name", :limit => 64 end end
テストが通らずなんでかなーと思って見たら何か意図した型でない状態になっていて気付く。
structure.sql なら安心。
CREATE TABLE `pref_master` ( `code` char(2) COLLATE utf8_unicode_ci NOT NULL, `name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL, PRIMARY KEY (`code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Rails の標準からはずれてくるといろいろつらいおもいするのでこういう事態を避けられるなら、そっちのほうがいいと思う。