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型にします。
class CreateUsers < ActiveRecord::Migration[6.0]
def change
create_table :users, id: :string do |t|
t.string :name
t.timestamps
end
end
end
create_table
でid: :string
と指定することでidの型をデフォルトのIntegerからStringに変更することができます。
2. Modelでcreate時にランダムな文字列のidを設定する
idをstring形式に変更したことで、Model.create時に自動でidに数字が付与されなくなります。
そのため、Model側でcreate時にランダムかつユニークな文字列を生成してセットする必要があります。
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.id
にSecureRandom.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
の降順で取得できるようにするためにはモデルファイルに以下のように設定を追加することで実装できます。
class User < ApplicationRecord
# User.allで取得するときに、レコード作成日の降順で取得する
default_scope -> { order(created_at: :desc) }
end