🤖

UUID主キーをActiveRecordで使う(PostgreSQL 13+)

2023/01/16に公開

Modelでの設定は不要で、カラムがPostgreSQLのUUID型で作成されていれば扱えます。

サンプルコード

db/migrate/20230115084803_create_backets.rb
class CreateBackets < ActiveRecord::Migration[7.0]
  def change
    create_table :backets, id: :uuid do |t|
      t.timestamps
    end
  end
end
db/migrate/20230115090623_create_items.rb
class CreateItems < ActiveRecord::Migration[7.0]
  def change
    create_table :items, id: :uuid do |t|
      t.string :name, null: false
      t.integer :price, null: false

      t.timestamps
    end
  end
end
db/migrate/20230115090735_create_backet_items.rb
class CreateBacketItems < ActiveRecord::Migration[7.0]
  def change
    create_table :backet_items, id: :uuid do |t|
      t.references :backet, null: false, foreign_key: true, type: :uuid
      t.references :item, null: false, foreign_key: true, type: :uuid
      t.index %i[backet_id item_id], unique: true
      t.integer :quantity, null: false

      t.timestamps
    end
  end
end

生成されるスキーマ

db/schema.rb
ActiveRecord::Schema[7.0].define(version: 2023_01_15_090735) do
  # These are extensions that must be enabled in order to support this database
  enable_extension "plpgsql"

  create_table "backet_items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.uuid "backet_id", null: false
    t.uuid "item_id", null: false
    t.integer "quantity", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["backet_id", "item_id"], name: "index_backet_items_on_backet_id_and_item_id", unique: true
    t.index ["backet_id"], name: "index_backet_items_on_backet_id"
    t.index ["item_id"], name: "index_backet_items_on_item_id"
  end

  create_table "backets", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "items", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
    t.string "name", null: false
    t.integer "price", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  add_foreign_key "backet_items", "backets"
  add_foreign_key "backet_items", "items"
end

default: "gen_random_uuid()" は不要

生成されるスキーマを見てわかるように、UUID生成のための gen_random_uuid() は自動で設定されるため、migrationにおいて default: "gen_random_uuid()" は不要です。

https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb#L48-L54

enable_extension 'pgcrypto' は不要

PostgreSQL 13以上では gen_random_uuid()pgcrypto 拡張は不要です。
PostgreSQL 12以下の場合は、 create_table id: :uuid の前に拡張の有効化が必要です。

enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')

https://www.postgresql.jp/document/13/html/functions-uuid.html

generatorによって作成されるmigrationの主キーのデフォルト

次のように設定しておくと作成されるmigrationの主キー型と外部キー型のデフォルトが :uuid になります。

config/application.rb
module MyApp
  class Application < Rails::Application
    # 省略
    config.generators do |g|
      g.orm :active_record, primary_key_type: :uuid
    end
  end
end

https://github.com/rails/rails/blob/v7.0.4/activerecord/lib/rails/generators/active_record/migration/migration_generator.rb#L11
https://github.com/rails/rails/blob/8015c2c2cf5c8718449677570f372ceb01318a32/activerecord/lib/rails/generators/active_record/migration.rb#L20-L28
https://github.com/rails/rails/blob/v7.0.4/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt#L3

参考

https://railsguides.jp/active_record_postgresql.html#uuid
https://www.postgresql.jp/document/13/html/functions-uuid.html

タケユー・ウェブ株式会社

Discussion