【Rails】URL の id を任意のカラム名(アカウント名など)に変更する

ユーザープロフィールなどを表示するときに、URLにはユーザーを識別するためのidが表示されます。

このidを、以下のようにアカウント名(ユーザー名)に変更して表示する方法についてまとめました。

http://localhost:3000/users/123

http://localhost:3000/users/sample

目次

開発環境

  • Ruby 3.1.2
  • Ruby on Rails 7.0.3
  • Bootstrap 5.1.3
  • M1 Macbook Air 2020
  • mac OS Monterey (ver. 12.4)
  • ターミナル bash (Rosetta 2 使用

出来上がりのイメージ

当記事では、以下のような出来上がりイメージを想定して進めていきます。

  • 新規アカウント登録直後は、プロフィールのURLには自動で生成されたアカウント名(ランダムな文字列)を表示する
  • アカウント設定画面で任意のアカウント名に変更する
  • 更新するとプロフィールのURLに設定したアカウント名が表示される

まず、ユーザーが新規アカウント登録した直後にログインしてプロフィール画面を開くと、以下のようにURLには自動で生成されたアカウント名が表示されるようにします。

次に、アカウント設定画面にて任意のアカウント名を変更できるようにします。

(アカウント設定画面を開くと、フォームにデフォルトのアカウント名が表示される)

任意のアカウント名を入力して更新。

更新後は、プロフィール画面およびアカウント設定画面のURLに任意で設定したアカウント名が表示されるようにします。

URLのidを任意のアカウント名に変更する流れ

  • Userモデルにアカウント名を格納するカラムaccount_nameを用意する
  • アカウント設定専用のコントローラーaccount_controllerを用意する
  • account_nameカラムにランダムな文字列を保存するメソッドを用意する
  • プロフィール画面およびアカウント設定画面のURLにユーザーidではなくaccount_nameを表示するようルーティングの設定を行う
  • アカウント設定用のフォームを用意する
  • アカウント設定画面にて任意のアカウント名に変更できるようにする(URLの表示も変わる)

今回は、プロフィール画面とアカウント設定画面を別々で作成するために、アカウント設定専用のコントローラーを追加して進めていきます。

Userモデルにaccount_nameカラムを追加

アカウント名を保存するためのaccount_nameカラムを追加します。

$ rails g migration add_account_name_to_users account_name:string

作成されたマイグレーションファイルを確認。

class AddAccountNameToUsers < ActiveRecord::Migration[7.0]
  def change
    add_column :users, :account_name, :string
  end
end

内容に間違いがなければマイグレーションを実行します。

$ rails db:migrate

アカウント設定用のコントローラー(account_controller.rb)を追加

アカウント設定専用のコントローラーaccount_controller.rbを追加します。

(必要となるテンプレートは編集画面用のeditのみ)

$ rails g controller account edit

アカウントの編集はusers_controller.rbだけでも可能ですが、今回はプロフィール用の編集画面とアカウント用の編集画面を別々で用意したいので、アカウント編集用に新たにコントローラーを追加して管理することにしました。

account_nameカラムにランダムな文字列を保存するメソッドを追加

account_nameカラムにランダムな文字列(アカウント名)を保存するメソッドをuser.rbに追加します。

class User < ApplicationRecord
  before_create :set_account_name

  ・・・

  private
    # ランダムなアカウント名を返す
    def set_account_name
      while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
      self.account_name = SecureRandom.base36
    end

end

追加する文字列のメソッドはSecureRandom.base36としました。

あわせて読みたい
【Rails】各種idをランダムな文字列に変換してPrimary keyとして使う Railsでデータベースに値を登録すると、デフォルトでは各テーブルのid(integer型)が1から順番に割り当てられるようになっています。 しかし、このままだと(以下の例...

ユーザーが新規アカウント登録するタイミングでaccount_nameに文字列を追加したいので、先ほど追加したメソッドをbefore_createでユーザー登録時に実行します。

URLにidではなくaccount_nameを表示するようルーティングを設定

通常、ユーザープロフィールのshowページのURLはユーザー識別のためにidを表示する設定になっていますが、param: :account_nameを追記することでURLにidの代わりにaccount_nameを表示できるようになります。

Rails.application.routes.draw do
  ・・・
  get "signup", to: "users#new"
  resources :users, except: [:new], param: :account_name
  resources :account, only: [:edit, :update, :destroy], param: :account_name
end

usersaccountの全ルーティングは以下の通りです。

      Prefix Verb    URI Pattern                          Controller#Action
      signup GET    /signup(.:format)                     users#new
       users GET    /users(.:format)                      users#index
             POST   /users(.:format)                      users#create
   edit_user GET    /users/:account_name/edit(.:format)   users#edit
        user GET    /users/:account_name(.:format)        users#show
             PATCH  /users/:account_name(.:format)        users#update
             PUT    /users/:account_name(.:format)        users#update
             DELETE /users/:account_name(.:format)        users#destroy
edit_account GET    /account/:account_name/edit(.:format) account#edit
     account PATCH  /account/:account_name(.:format)      account#update
             PUT    /account/:account_name(.:format)      account#update
             DELETE /account/:account_name(.:format)      account#destroy

また、URLにaccount_nameを表示するためのメソッドをuser.rbに記述します。

class User < ApplicationRecord
  before_create :set_account_name

  ・・・

  #---- 追記 -----
  def to_param
    account_name
  end
  #---------------

  private
    # ランダムなアカウント名を返す
    def set_account_name
      while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
      self.account_name = SecureRandom.base36
    end

end

これで、プロフィール画面を開いたときに、URLにidではなくてaccount_name(ランダムな文字列)が表示されればOKです。

account_nameカラムのバリデーションを追加

account_nameカラムのバリデーションをuser.rbに追記します。

class User < ApplicationRecord
  before_create :set_account_name

  ・・・

  #----------------------- 追記 ----------------------------
  VALID_ACCOUNT_NAME_REGEX = /\A[a-zA-Z0-9]+\z/  # 半角英数字のみ受け付ける正規表現
  validates :account_name, uniqueness: true,
                           length: { minimum:3, maximum: 50 },
                           format: { with: VALID_ACCOUNT_NAME_REGEX }, 
                           on: :update # updateアクションにのみバリデーションを適用する
  #---------------------------------------------------------

  ・・・

  def to_param
    account_name
  end

  private
    # ランダムなアカウント名を返す
    def set_account_name
      while self.account_name.blank? || User.find_by(account_name: self.account_name).present? do
      self.account_name = SecureRandom.base36
    end

end

バリデーションにon: :updateと記述することで、ここで設定したバリデーションをupdateアクションにのみ適用することができます。

createアクションにも適用してしまうと、バリデーションエラーでaccount_nameが自動で生成されなくなってしまうため)

account_name編集用のフォームを作成する

account_name編集用のテンプレートを作成します。

<h1 class="text-center">アカウント設定</h1>
<div class="row justify-content-center">
  <!------------- url: account_path(params[:account_name]) を追記 ------------->
  <%= form_with(scope: :account, model: @user, url: account_path(params[:account_name]), local: true, class: "mt-4 form-column-width") do |f| %>
  
  ・・・

    <!----- アカウント名 ------>
    <div class="mb-3 <%= @user.errors.include?(:account_name) ? "validation_errors" : "" %>">
      <%= f.label "アカウント名変更", class: "form-label fw-bold"%>
      <div class="input-group shadow-sm rounded">
        <span class="input-group-text" id="basic-addon1">
          <i class="bi bi-at"></i>
        </span>
        <%= f.text_field :account_name, class: "form-control", placeholder: "アカウント名を入力してください" %>
      </div>
      <div id="emailHelp" class="form-text text-primary">※3文字以上のユニークなアカウント名(半角英数字)を入力してください</div>
      <%= render 'layouts/error_messages', obj: @user, key: :account_name %>
    </div>

    ・・・

 <% end %>
</div>

必要に応じてパスワードの入力欄を設けても良いでしょう。

最後に、account_controller.rbupdateアクションに以下のように記述します。

class AccountController < ApplicationController
  before_action :set_user

  ・・・

  def update
    if params[:account][:account_name].blank?
      @user.errors.add(:account_name, :blank)
      render "edit", status: :unprocessable_entity
    elsif @user.update(user_params)
      flash[:notice] = "アカウント情報が更新されました"
      redirect_to @user
    else
      render "edit", status: :unprocessable_entity
    end
  end

 ・・・

  private
    def set_user
      @user = User.find_by(account_name: params[:account_name])
    end

    def user_params
      params.require(:account).permit(:account_name, ...)
    end

end

これで、アカウント設定画面から任意のアカウント名に変更できるようになりました。

以上です。

参考記事

Qiita
【Ruby on Rails】URLのidをカラム名に変更 - Qiita はじめにRailsでは、routes.rbでresourcesメソッドを使用すると、通常:idパラメータがデフォルトで使われますが、場合によっては表示を変更したい場合もあるかと思います。...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次