2017-07-16 1 views
1

In meiner Anwendung habe ich eine Case Modell & jeden Fall vom Benutzer generiert hat eine case_number.Ruby on Rails - Generierung von Zufallszahl, die nicht in der Datenbank existiert

case_number wird von meinem Case Modell generiert. Momentan erstelle ich eine 3-stellige Zufallszahl aus meinem Modell, nachdem ich eine case erstellt habe.

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 

Dies verursacht haben so einige der Fälle haben die gleiche case_number. Wie kann ich von meinem Modell eine Zufallszahl erzeugen, die noch nicht verwendet wurde & Wenn alle 3 Ziffern genommen wurden, kann es sich auf 4 Ziffern verschieben und so weiter.

+0

Warum eine drei Zufallszahl und (wenn alle drei Stellen genommen werden) Schalter auf vier Ziffern Zufallszahl? Warum eine sequenzielle ID verwenden oder mit einer längeren Zufallszahl beginnen? Warum brauchen Sie Zufälligkeit, wenn das Erraten einer Zahl noch möglich wäre? – spickermann

+0

@spickermann Danke für den Kommentar. Ich kann 4 Ziffern verwenden, aber wie gesagt Wenn ich 3 Ziffern verwendet habe, dann hatten einige der Fälle die gleiche 'Fallnummer', also möchte ich sicherstellen, dass sie uniq sind. Zufälligkeit ist notwendig, weil ich die Zahlen zufällig sein muss, damit die Fälle nicht von 1,2, 3, 4 ... gehen. – Rubioli

+0

IMHO machen die Fallnummer "zufällig" aber nur 3 oder für Ziffern ist eine seltsame Anforderung . Interessieren Sie sich für Details? –

Antwort

0

Um eine Zufallszahl und die Sicherstellung zu erzeugen, dass die Zahl nicht in der Datenbank vorhanden ich so etwas tun würde:

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 

Bitte beachten Sie, dass je mehr Fall in der Datenbank desto länger ist es könnte nehmen Sie eine unbenutzte Nummer. Daher schlage ich vor, von Anfang an größere Zahlen zu verwenden. Größere Zahlen haben einen weiteren Vorteil: Sie sind schwieriger zu erraten, was in Ihrer Anwendung wichtig sein könnte oder nicht.

Außerdem: Rails kann diese Eindeutigkeit in der Datenbank nicht garantieren. Es könnte Race Conditions geben, die zu Duplikaten führen. Der einzige Weg, dies zu vermeiden ist es, einen eindeutigen Index der Datenbankspalte in einer wie diese Migration hinzuzufügen:

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

Danke ein Haufen @spickermann, funktioniert wie ein Charme :) – Rubioli

+0

Sobald alle Fallnummern aufgebraucht sind, wird dies für immer laufen. Und wie @spickerman erklärte: Das ist nicht effizient, vor allem wenn immer mehr Fälle in der DB sind. Auch: da die case_number automatisch generiert wird, fügt die Validierung keinen Wert hinzu, es verlangsamt nur die Dinge, indem sie eine andere Auswahl ausgibt, um die Eindeutigkeit einer Zahl sicherzustellen, die eindeutig ist. –

1

zunächst sicherstellen, dass case_number einen eindeutigen Index in der DB hat.

rails generate migration add_unique_index_to_case_number

bearbeiten die erzeugte Migration dies etwas aussehen:

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

Dies stellt sicher, (ungültig (duplicate) Fälle fallen, bevor Sie diesen Index hinzufügen können, müssen eventuell) dass der DB keine ungültigen Fallnummern hinzugefügt werden können.

Dann würde ich eine fortlaufende Nummer nicht zufällig verwenden. Wenn schließlich alle Zahlen von 100 - 999 verwendet werden, ist es wenig sinnvoll, sie zufällig zu machen (und es gibt nicht viel Entropie in 3 Ziffern, so dass es leicht ist, die Zahl zu erraten). Es ist auch ein wenig umständlich, beim Einfügen eine nicht existierende "zufällige" Nummer zu wählen.

rails generate migration add_sequence_to_case_numbers 

Und wieder die Migration ändern:

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