2017-07-16 99 views
1

在我的应用程序中,我有一个Case模型&用户生成的每种情况都有一个case_numberRuby on Rails - 生成数据库中不存在的随机数

case_number从我的Case模型生成。目前我创建一个case后,从我的模型创建一个3位数的随机数。

class Case < ActiveRecord::Base 

    after_create :random_case_number 

    private 
    def random_case_number 
     random_number = Random.rand(999) 
     self.update_attributes(case_number: random_number) 
    end 
end 

这导致所以一些案件有相同的case_number。如何从我的模型中生成一个当前未使用的随机数字&如果所有3位数字都被采用,它可以移动到4位数字等等。

+0

为什么要用三个随机数,(当采取一切三位数)切换到四位随机数字?为什么使用顺序ID或从更长的随机数开始?如果猜测一个数字仍然可能,为什么你需要随机性? – spickermann

+0

@spickermann感谢您的评论。我可以使用4位数字,但如上所述当我使用3位数字时,即使这样,一些案例也有相同的'case_number',所以我想确保它们是uniq。随机性是需要的,因为我需要的数字是随机的,所以案件不会从1,2,3,4 ... – Rubioli

+0

恕我直言,使案件号码“随机”,但只有3或数字是一个奇怪的要求。谨慎提供一些细节? –

回答

0

要生成一个随机数,并确保该号码不存在于数据库中,我会做这样的事情:

before_create :assign_unique_case_number 

validates! :case_number, uniqueness: true 

private 

CASE_NUMBER_RANGE = (10_000..99_999) 

def assign_unique_case_number 
    self.case_number = loop do 
    number = rand(CASE_NUMBER_RANGE) 
    break number unless Case.exists?(case_number: number) 
    end 
end 

请注意,更多的情况下,也有在数据库中它可能较长采取找到一个未使用的号码。因此,我建议从一开始就使用更大的数字。更多的数字还有另外一个优点:他们很难猜测应用程序中可能或不可能重要的内容。

此外:Rails不能保证数据库中的唯一性。可能存在导致重复的竞赛条件。避免的唯一途径是唯一索引,在这样的迁移添加到数据库列:

add_index :cases, :case_number, unique: true 
+0

感谢@spickermann,像一个魅力:) – Rubioli

+0

一旦所有的案例数量用完,这将永远运行。正如@spickerman解释的那样:这不是有效的,特别是当数据库中有越来越多的案例时。 另外:因为case_number是自动生成的,所以验证不会增加值,它只是通过发出另一个select来确保唯一性(它无法真正确保)已确保唯一的数字,从而减慢速度。 –

1

首先确保case_number在一个数据库中的唯一索引。

rails generate migration add_unique_index_to_case_number

编辑生成的迁移有点像这样:

class AddUniqueIndexToCaseNumbers < ActiveRecord::Migration[5.1] 
    def change 
    add_index(:cases, :case_number, unique: true) 
    end 
end 

(您可能需要丢弃无效(一式两份)的情况下,才能添加这个索引)

这保证没有无效的案件号码可以添加到数据库。

然后我会使用一个序列号而不是随机的。如果最终所有的数字从100到999都被使用了,没有太大意义使它随机(并且3位数字没有太多的熵,所以很容易猜出数字)。插入时选择不存在的“随机”数字也有点麻烦。

rails generate migration add_sequence_to_case_numbers 

并再次修改迁移:

class AddSequenceToCaseNumbers < ActiveRecord::Migration[5.1] 

    def change 
    execute <<-SQL 
     CREATE SEQUENCE cases_case_number_seq; 
     ALTER TABLE cases ALTER COLUMN case_number SET DEFAULT nextval('cases_case_number_seq'); 
    SQL 
    end 
end