LoginSignup
13
13

More than 3 years have passed since last update.

【Rails】ランダムな文字列をPrimary keyとして使う

Posted at

Abstract

Ruby on Railsでは、モデルを作成するときデフォルトでInteger型のidをprimary keyとして付与してくれる。
一方で、このQiitaの記事のURLのように、ランダムな文字列をprimary keyに用いたいケースもある。
この記事では、デフォルトで作成されるInteger型のidの代わりに、String型でランダムな文字列のidをprimary keyとする実装手順をメモする。

Environments

  • ruby: 2.6.5
  • rails: 6.0.0

0. Userモデルを作成する。

今回はサンプルとして、Userモデルをscaffoldで作成する。

$ rails g scaffold User name:string

1. migrationでidの型を指定する

まずはmigrationファイルをいじり、idをString型にします。

db/XXXXXXXXXXXXXX_create_user.rb
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users, id: :string do |t|
      t.string :name

      t.timestamps
    end
  end
end

create_tableid: :stringと指定することでidの型をデフォルトのIntegerからStringに変更することができます。

2. Modelでcreate時にランダムな文字列のidを設定する

idをstring形式に変更したことで、Model.create時に自動でidに数字が付与されなくなります。
そのため、Model側でcreate時にランダムかつユニークな文字列を生成してセットする必要があります。

app/models/user.rb
class User < ApplicationRecord
  # create前にset_idメソッドを呼び出す
  before_create :set_id

  private
    def set_id
      # id未設定、または、すでに同じidのレコードが存在する場合はループに入る
      while self.id.blank? || User.find_by(id: self.id).present? do
        # ランダムな20文字をidに設定し、whileの条件検証に戻る
        self.id = SecureRandom.alphanumeric(20)
      end
    end
end

まずプライベートメソッドとして、set_idメソッドを作成します。
whileの条件として、self.id.blank? || User.find_by(id: self.id).present?を定義していますが、つまり「idがまだ設定されていない、または、すでに同じidのレコードが存在する」場合にループに入るようになっています。
whileのループの中ではself.idSecureRandom.alphanumeric(20)を用いて20文字のランダムな文字列(大文字小文字アルファベット+数字)を生成し、またwhileの条件式と検証します。
SecureRandomはrubyのライブラリです。他にも16進数からランダムに文字列を作るSecureRandom.hex()やUUID version4を作成するSecureRandom.uuidなどがあるので用途に合わせて設定するといいです。--> module SecureRandom (Ruby 2.6.0)

このメソッドをbefore_createコールバックで呼び出します。
before_createコールバックはモデルのcreate直前に呼び出され、updateの場合は呼び出されません。
updateの度にidが変わってしまってはいけないのでbefore_createコールバック時にset_idメソッドを呼び出します。

(additional)作成順に取得する

今のままだとUser.allでレコードを取得しようとした時にupdated_atの昇順で並んでいます。
例えば、created_atの降順で取得できるようにするためにはモデルファイルに以下のように設定を追加することで実装できます。

app/models/user.rb
class User < ApplicationRecord

  # User.allで取得するときに、レコード作成日の降順で取得する
  default_scope -> { order(created_at: :desc) }

end

Reference

13
13
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
13
13