Vitalina
Vitalina

Reputation: 51

Rails create unique value for saving in database

I mean not to check on saving is current value - unique.

How can I create value (for example in my model), check is it unique for my table. Anf id not than try to create new value maybe. And check the uniqueness again

Now I have the following code in my model:

  after_initialize :init

  def init
    self.some_field = rand(1..99_999_999).to_s
  end

Upvotes: 0

Views: 857

Answers (4)

Avijit Majhi
Avijit Majhi

Reputation: 518

You can use model callback like,

before_create :generate_some_field

def generate_some_field
  self.some_field = loop do
    random = rand(1..99_999_999).to_s
    break random unless ModelName.exists?(some_field: random)
  end
end

Upvotes: 2

inem
inem

Reputation: 116

I recently wrote a gem to solve this problem for myself: https://github.com/inem/shufflino

No retries or extra SQL query needed with this approach.

I also recommend watching this talk on the topic.

Upvotes: 1

Enric Tordera
Enric Tordera

Reputation: 99

Add a validation for uniqueness, and initialize the field before validation. Add a max retries limit to avoid possible deadlocks when number of records approaches or reaches max number of possible random values.

  INITIALIZATION_MAX_RETRIES = 10
  before_validation :initialize_fields

  validates_uniqueness_of :some_field

  private

  def initialize_fields
    unless some_field
      retries = 0
      loop do
        retries += 1
        break if retries > INITIALIZATION_MAX_RETRIES

        self.some_field = rand(1..99_999_999).to_s
        break unless self.class.find_by(some_field: some_field)
      end
    end
  end

Upvotes: 1

dbugger
dbugger

Reputation: 16464

Add a validation

Then set the value, check it,

if good: it's done.

if not: try again, repeating a reasonable number of times.

Something like this

 validates :some_field, uniqueness: true 

 def init
    attempts = 0
    loop do
      self.some_field  <<  SecureRandom.uuid.to_s[0..16]
      self.valid?
      break unless self.errors[:some_field].any?
      attempts = attempts + 1
      if attempts > 3
         self.errors[:some_field] << "Could not generate a unique value after #{attempts} attempts"
         break
      end
    end

  end

Upvotes: 1

Related Questions