3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】主キーを変更せずに公開URLをuuid化する(ActiveStorage対応)

Last updated at Posted at 2025-12-12

262EE380-2638-41E5-82D5-634388B27EA1.png

経緯

投稿型のアプリにて、投稿の詳細画面のURLをpost/1といったURLでなく、post/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxといった推測困難なURLにすべく主キーのuuid化を検討していました。
しかし、今回uuid化を行いたいpostsテーブルの主キーは、

  • Bookmark機能に使われるpost_id
  • ActiveStorageによる画像添付に使われるpost.id

といった依存関係が既に存在し、この状態で主キーをuuidに変更すると関連するすべての外部キーをuuidに変更する必要があります。

特にActiveStorageの場合は、例えば以下のテーブルもuuid前提で作り直す必要があります。

  • active_storage_attachments.record_id
  • active_storage_variant_records.blob_id
  • active_storage_blobs.id

Rails標準のActiveStorageはbigint前提で設計されているため、途中からuuid化を行うと、既存の画像とレコードの紐付けが切れるなど本番データを破壊するリスクが高く、あまり現実的ではありません。

私の場合、ブックマーク機能はcurrent_userのみアクセス出来る設計ということもあり、今回はpost.idはそのまま残し、postsテーブルにuuidカラムを追加し公開URLのみuuidで生成する方式を採用しました。

実装内容

  • 外部公開URL(誰でもアクセス可能なページ)はuuidを使用
  • 内部処理(ActiveStorage・Bookmark等の参照)は従来通りpost.idを使用

環境

  • Windows
  • WSL2
  • Docker
  • Rails 7.2
  • Ruby 3.3
  • PostgreSQL

実装手順

1. uuidカラムの追加

マイグレーションファイルを生成しpostsテーブルにuuidカラムを追加します。

class AddUuidToPosts < ActiveRecord::Migration[7.2]
  def change
    ## PostgreSQLのuuid生成機能(pgcrypto)を有効化
    enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto')

    # postsにuuidカラムを追加(レコード作成時に自動生成、nullを許可しない)
    add_column :posts, :uuid, :uuid, default: "gen_random_uuid()", null: false
    # uuidに一意性を持たせる
    add_index :posts, :uuid, unique: true
  end
end

マイグレーションを実行すると以下の内容がpostsテーブルに反映され、postsテーブルにuuidカラムが追加されたことが確認できます。

create_table "posts", force: :cascade do |t|
    # 省略
    t.uuid "uuid", default: -> { "gen_random_uuid()" }, null: false
    # 省略
    t.index ["uuid"], name: "index_posts_on_uuid", unique: true
  end

2. postsのルーティングをuuidベースに変更

現在ルーティングの生成はpost.idベースになっているのでconfig/routes.rbを編集してuuidベースでルーティングが行われるようにします。

# config/routes.rb

# param: :uuidを追加
resources :posts, param: :uuid, only: [ :new, :create, :show, :index, :edit, :update, :destroy ]

編集後ルーティングテーブルを確認してみると、postsのルーティングがuuidベースに変わっていることが確認できました。

post GET    /posts/:uuid(.:format)
     PATCH  /posts/:uuid(.:format)
     PUT    /posts/:uuid(.:format)
     DELETE /posts/:uuid(.:format)

3. リンクの生成がuuidで行われるように修正

ルーティングがuuidベースになっても、リンクの生成がidベースのままなのでuuidを使って生成されるようmodels/post.rbを編集します。

# models/post.rb

def to_param
  uuid
end

RailsはURL生成にto_paramというメソッドを使用しています。
今回はuuidでURLを生成したいので、post.rbにてto_paramの値をオーバーライドします。
この記述により、Post/:uuidという形でURLが生成されるようになります。


4. 変数がuuidを取得出来るようにする

現在はレコードの取得をidを使って行っているので、公開URLで表示されるものはuuidでレコードを取得できるようにコントローラーを修正します

変更前

def show
  @post = Post.find(params[:id])
end

変更後

def show
  @post = Post.find_by(uuid: params[:uuid])
end

その他のアクションにおける@postの値も同じようにuuidで取得出来るよう編集します。


これでpostsのURLがuuidで表示され、関連するテーブルとのやりとりはpost.idで行うという実装が完了しました。

私の場合は以上の作業で目的とするuuid化を実装出来ましたが、例えばpostsに関連する機能が誰でもアクセスできる場合など、設計によっては関連テーブル側にもuuidカラムを追加し、公開用IDと内部IDを分けて管理するなどの必要があります。

また、ActiveStorage を使用していない場合や、アプリを新規に構築している段階であれば、主キーごとuuidに変更するという選択肢も取り得ると考えています。

参考記事

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?