はじめに
通常 Rails では各テーブルの主キーを id
カラムに設定します。
ただ、例えば既にあるデータを import するときなど、Rails の規約通りにテーブルを設計できないときには id
カラムを作らずに別のカラムを主キーとしたいときもあると思います。
その際は migration ファイルに書く create_table
メソッドのオプションでよしなに設定してやれば良いのですが、そのオプションの書き方が勘違いを起こしやすいもので、少し詰まってしまったのでここにまとめたいと思います。
create_table
のオプション
create_table
について Rails ドキュメントを見てみると、以下のように書いてあります。
create_table(テーブル名 [, オプション])
create_table テーブル名 [, オプション] do |t| t.型 カラム名 [, カラムオプション] end
オプション | 説明 | デフォルト |
---|---|---|
:id | 主キーを自動生成 | true |
:primary_key | 主キーのカラムの名前 | id |
つまり、:id
オプションがデフォルトで true に、:primary_key
カラムがデフォルトで id
になっているため、Rails ではテーブルを作成すると勝手に id
カラムが作られて、その id
カラムがそのテーブルの主キーとなります。
id: false にすると...
では :id
オプションを false に設定するとどうなるのか。
実際に以下のような migration ファイルを書いてみます。
class CreateHoges < ActiveRecord::Migration[5.2] def change create_table :hoges, id: false do |t| t.integer fuga end end end
すると、:id
オプションを false にしたため id
カラムは作られません。
また :primary_key
オプションはデフォルトのままなので id
カラムを指しますが、id
カラムが存在しないため、主キー はこの hoges
テーブルには設定されないことになります。
起きた勘違い
ではここで先述のような理由で、id
カラムを作らずにかつ fuga
カラムを主キーにしたい場合を考えます。
ここで私は以下のように考えてしまいました。
id
カラムを作りたくないためid: false
とする- 主キーを
fuga
カラムにしたいためprimary_key: :fuga
とする
そして以下のように migration ファイルを書きました。
class CreateHoges < ActiveRecord::Migration[5.2] def change create_table :hoges, id: false, primary_key: :fuga do |t| t.integer fuga end end end
しかし、データベースを確認してみると id
カラムは作られていませんでしたが、なんと主キーも設定されていないことが分かりました。
正しい書き方
では、id
カラムを作らずに fuga
カラムを主キーにしたい場合にはどう書けば良いのか。
結論から言えば、以下のように書くと上手くいきます。
class CreateHoges < ActiveRecord::Migration[5.2] def change create_table :hoges, primary_key: :fuga end end
つまり、わざわざ id: false
などと書いたり create_table
にブロックを渡さなくても :primary_key
オプションに fuga
を設定してあげるだけで、fuga
カラムが作られて主キーに設定される上に id
カラムも作られません。
実際に Rails のソースコードにも以下のような example が書いてありました。
# ====== Rename the primary key column # # create_table(:objects, primary_key: 'guid') do |t| # t.column :name, :string, limit: 80 # end # # generates: # # CREATE TABLE objects ( # guid int(11) DEFAULT NULL auto_increment PRIMARY KEY, # name varchar(80) # )
つまり、「id
カラムのカラム名を変更する」という形で望まれる挙動を実現するようです。
さらに、以下のような example もありました。
# ====== Do not add a primary key column # # create_table(:categories_suppliers, id: false) do |t| # t.column :category_id, :integer # t.column :supplier_id, :integer # end # # generates: # # CREATE TABLE categories_suppliers ( # category_id int, # supplier_id int # )
つまり、id: false
とは「id
カラムを作るな」という意味ではなく「主キーを設定するな」という意味だそうです。
そのため、id: false, primary_key: :fuga
と書くと id: false
が読み込まれて「主キーを設定するな」という命令が走ってしまうことになり、上手く主キーが設定されないようでした。
複合主キーにしたい場合
もう一度 id
カラムを作らずに fuga
カラムを主キーにしたい場合の正しい書き方を見てみます。
class CreateHoges < ActiveRecord::Migration[5.2] def change create_table :hoges, primary_key: :fuga end end
主キーが1つのときには上記のように :primary_key
オプションを設定するだけで OK でした。
これが、複合主キーを設定したいときには実は少し挙動が変わります。
例えば id
カラムを作らずに fuga
カラムと piyo
カラムを複合主キーとしたいときには以下のように書くことになります。
class CreateHoges < ActiveRecord::Migration[5.2] def change create_table :hoges, id: false, primary_key: [:fuga, :piyo] do |t| t.integer fuga t.integer piyo end end end
つまり、:primary_key
オプションには主キーにしたい fuga
と piyo
を配列で渡して、かつ create_table
に渡すブロックの中にも型と共にカラム名を書いてあげないといけません。
これは恐らくですが、主キーが1つのときは先述の通り「id
カラムのカラム名を変更する」のに対して、複合主キーの場合は別の処理で望まれる挙動を実現するのだと考えられます。
終わりに
Rails で id
以外を主キーに設定したいときにどうすれば良いのかについて書きました。
オプションの書き方が少し非直感的なので、私のように誤解する方も居ると思います。
そのような方に読んでいただけたら幸いです。