KaQiita

新米社会人が適当なことを書いてます。温かく見守ってやってください。

Rails で id 以外を主キー(primary_key)に設定する

はじめに

通常 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 オプションには主キーにしたい fugapiyo を配列で渡して、かつ create_table に渡すブロックの中にも型と共にカラム名を書いてあげないといけません。

これは恐らくですが、主キーが1つのときは先述の通り「id カラムのカラム名を変更する」のに対して、複合主キーの場合は別の処理で望まれる挙動を実現するのだと考えられます。

終わりに

Railsid 以外を主キーに設定したいときにどうすれば良いのかについて書きました。

オプションの書き方が少し非直感的なので、私のように誤解する方も居ると思います。

そのような方に読んでいただけたら幸いです。