Rails: リソースの一括登録画面: 3: 同一メールアドレスの登録防止

前回の続きです。同じメールアドレスを登録しようとすると画面にエラーメッセージを表示するようにします。それとDBの一意性バリデーションを追加します。

方針

メールアドレスの重複についてモデルとDBで一意性バリデーションをかける。

モデル

同じメールアドレスが存在すれば save!で ActiveRecord::RecordInvalid が発生する。
画面に該当のモデルのエラーメッセージを表示する。

DB

モデルのバリデーションが通った後に別端末などで同じメールアドレスが登録される場合がある。
トランザクションの完了時に ActiveRecord::RecordNotUnique が発生するので、画面に alert メッセージを表示する。

実装

DBの一意性バリデーションを追加します。

bin/rails g migration AddIndexToAccounts
class AddIndexToAccounts < ActiveRecord::Migration[7.0]
  def change
    add_index :accounts, :email, unique: true
  end
end
bin/rails db:migrate

app/views/bulk_accounts/new.html.erb
notice と alert を表示できるようにします。

<p style="color: green"><%= notice %></p>
<p style="color: red"><%= alert %></p>

<h1>New accounts</h1>

...

app/controllers/bulk_accounts_controller.rb
save を save! に変更して例外を発生させます。

  def create
    is_success = false
    @accounts = accounts_params.map { Account.new(_1).tap(&:valid?) }
    ActiveRecord::Base.transaction do
      is_success = @accounts.none? { _1.errors.any? } and @accounts.all?(&:save!)
      raise ActiveRecord::RecordInvalid unless is_success
    end
  rescue ActiveRecord::RecordInvalid
    is_success = false
  rescue ActiveRecord::RecordNotUnique
    is_success = false
    flash.now[:alert] = "Account already exists."
  ensure
    if is_success
      redirect_to new_bulk_account_url, notice: "Accounts were successfully created."
    else
      render :new, status: :unprocessable_entity
    end
  end

補足

一時的にモデルのバリデーションをコメントアウトして、トランザクションの完了時に ActiveRecord::RecordNotUnique を発生させ、画面に alert メッセージを表示させます。

class Account < ApplicationRecord
  validates :name, :email, presence: true
  # validates :email, format: { with: URI::MailTo::EMAIL_REGEXP }, uniqueness: true
end

課題

  • 見栄え(スタイリング)を良くしたい。

  • Account の登録数を増減できません。

以上です。

続きを書きました。

この記事が気に入ったらサポートをしてみませんか?