質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Devise

Deviseとは、Ruby-on-Railsの認証機能を追加するプラグインです。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Q&A

1回答

3238閲覧

主キーとして使えるユニークでランダムな数字だけの乱数を作成し、被ったら再生成できるようにしたい

zendendo

総合スコア43

Devise

Deviseとは、Ruby-on-Railsの認証機能を追加するプラグインです。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Ruby on Rails

Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

0グッド

2クリップ

投稿2017/11/15 06:55

編集2017/11/15 07:10

###・前提・実現したいこと
ruby(rails)で1:1の関連付けをしたユーザーモデルと口座モデルがあり、
deviseでユーザーが新規会員登録と同時に自動的に口座を作成する実装をしました。
このときランダムかつユニークで主キーとなり得る11桁の数字だけの乱数(口座番号)を作成して登録するのですが、
万が一に口座番号が被って登録ができなかった場合、再び乱数を生成して登録を成功させるように
するにはどうすればよろしいでしょうか?

###現状のコード
・ユーザーモデルと口座モデルは1:1で関連付けをしてあります。
・deviseのカスタマイズした新規会員登録コントローラに、口座への同時作成の指示を記述しています。
・口座モデルには、口座番号がユニークであるかどうかを検証しています。

app/controllers/users/registrations_controller.rb

class Users::RegistrationsController < Devise::RegistrationsController def new super end # POST /resource def create super #Userの子要素であるbasic_income_accountを関連付ける resource.build_basic_income_account #同時につくられるBI口座の登録内容 #12桁のランダムでユニークな口座番号を作成する require 'securerandom' n = 12 resource.basic_income_account.account_number = format("%0#{n}d", SecureRandom.random_number(10**n)) #同時に作られる口座の残高は0とする。 resource.basic_income_account.balance = 0 resource.save end

/home/vagrant/megterra/app/models/basic_income_account.rb

class BasicIncomeAccount < ApplicationRecord #BasicIncomeAccountはuserから1:1の関係で所有されている belongs_to :user #ユーザーIDが存在するなら検証成功 validates :user_id, presence:true #口座番号が存在し、値(口座番号)がユニーク(被らない番号)なら検証成功 validates :account_number, presence: true, uniqueness: true #口座残高は数値か小数点のみ有効 validates :balance, presence: true, numericality: true end

###補足情報(言語/FW/ツール等のバージョンなど)
Rails 5.1.3
ruby 2.4.1
devise (4.3.0)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

口座番号が11桁か12桁かで表記がゆれているようです。
意図的に重複を起こす等して、そのような場合にコードがどのような挙動となるかを確認されると、重複した場合のリカバリ処理が書けるかと思います。
おそらく主キーの重複に関する例外が発生するのではないかと思いますので、例外が発生した場合に再処理を行うといった形が考えられます。

口座番号が完全にランダムな数値となる場合、正しい口座番号かどうかは必ずデータベースで確認しなくてはいけません。
チェックディジットを付与するなどすると、口座番号自体からある程度適切な番号かどうかが確認ができるようになるので、多少チェックしやすくなります。
また、何らかの理由で口座番号を変更する理由が発生した場合のことも考えると、口座番号はユニークではあっても主キーとしての使用はしない方がよい場合もあるかもしれません。

投稿2017/11/15 21:43

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

zendendo

2017/11/16 01:47 編集

例外処理のテストとして既に登録済みの口座番号を入れて、 例外処理を発生させようとしたのですが例外処理は実行されず、新規会員登録だけ行われました。 おそらくアプリが「口座番号が被って口座登録ができなければ例外」という認識を持って いないのではないかと思うのですが、この場合どうやってアプリに認識させればいいのでしょうか? def create super #Userの子要素であるbasic_income_accountを関連付ける resource.build_basic_income_account #以下の例外処理では、口座番号が被ってしまったときに、登録が成功するまで再度番号を作成して登録作業を続けるとする begin #登録済み番号を代入して例外を発生させる resource.basic_income_account.account_number = '231229948564' rescue #例外が発生した場合の処理を書く require 'securerandom' n = 12 resource.basic_income_account.account_number = format("%0#{n}d", SecureRandom.random_number(10**n)) end #同時に作られる口座の残高は0とする。 resource.basic_income_account.balance = 0 resource.save end
退会済みユーザー

退会済みユーザー

2017/11/16 02:05

DB側でユニーク制約をつけることで対応できるのではないかと思います。 add_index :account_number, unique: true 主キーとして設定しているはずなのに同じ口座番号が登録できてしまっている場合、実際には主キーとしての設定がされていない可能性があります。
zendendo

2017/11/16 11:45 編集

suyamaさん。 アドバイスありがとうございます。 質問の前提を崩してしまってすみませんが、 アドバイスを参考に主キーの変更はやめて、railsがデフォルトで作成してくれるidを主キーのままにしました。 ただし、口座番号は電話番号の様にユニークな(被ることのない)数字だけの文字列にしたいと思います。 models/basic_income_account.rb(口座モデルファイル)には、 validates :account_number, presence: true, uniqueness: true と記述し、口座番号がユニークじゃないと登録できないようにしてあります。 registrations_controller.rbに例外処理を記述しました(例外処理の記述内容は、2017/11/16 10:47のコメントに書いた通りです) 例外を発生させようと既に登録済みの番号を入れたのですが、 例外処理(新しく乱数を作成してresource.basic_income_account.account_numberに代入する)は行われず、 口座は作成されませんでした。 ということは、同じ口座番号は登録できないようにはなっているみたいです。 しかし、登録されないのならば例外処理を実行するハズなのですが、していないみたいです。 蒸し返し的な質問ですみませんが、 どうすれば例外処理が正しく行われるようになるのでしょうか?
mingos

2017/11/16 08:45

横からですが、保存する処理は、必ずビックリマーク付きのメソッドで実行してください。 save!, create!, update! です。 ビックリマーク付きのメソッドは、rubyの文化ではオブジェクトの内容が変更される、または失敗時に例外が発生するというメソッドの印として用いられます。 railsもこれに従っていて、ビックリマークなしのsaveは結果をtrue/falseで返すだけで例外は発生させません。
zendendo

2017/11/16 12:13 編集

mingosさん、アドバイスありがとうございます。 saveの部分をsave!に書き換えました。 さらに、account_numberの作成部分だけでなく、balanceと保存を実行するsave!も begin以下(エラーが発生する可能性のある処理として)に記述しました。 しかし、それでも新規会員登録だけが成功し、口座登録はできませんでした。 def create super resource.build_basic_income_account begin #例外を起こす処理 resource.basic_income_account.account_number = '231229948564' resource.basic_income_account.balance = 0 resource.save! rescue #例外発生時の処理。 require 'securerandom' n = 12 resource.basic_income_account.account_number = format("%0#{n}d", SecureRandom.random_number(10**n)) resource.basic_income_account.balance = 0 resource.save! end end
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問