Обеспечить уникальное значение столбца на уровне приложения - PullRequest
3 голосов
/ 28 сентября 2011

Одна из моделей в приложении на Rails 3.1, над которым я работаю, имеет атрибут «код», который генерируется автоматически при создании записи и должен быть уникальным. Приложение должно проверить базу данных, чтобы увидеть, существует ли сгенерированный код, и, если это так, должно сгенерировать новый код и повторить процесс.

Я могу обеспечить уникальность поля на уровне базы данных с помощью add_index :credits, :code, :unique => true (что я делаю), а также в модели с validates_uniqueness_of, но оба они просто вернут ошибку, если сгенерированный код существует. Мне нужно просто повторить попытку в случае дубликата. Сгенерированные коды достаточно длинные, поэтому дубликаты маловероятны, но я должен быть уверен на 100%.

Эта генерация кода прозрачно обрабатывается для конечного пользователя, поэтому он никогда не должен видеть ошибку. После того, как код сгенерирован, каков наилучший способ проверить, существует ли он, и повторять процесс, пока не будет найдено уникальное значение?

Ответы [ 2 ]

4 голосов
/ 28 сентября 2011

Вот краткий пример, технически здесь все еще есть условие гонки, хотя, если вы не видите сотен или тысяч созданий в секунду, это действительно не должно вызывать беспокойства, в худшем случае ваш пользователь получит ошибку уникальности, если два создания запущены втаким образом, что они оба выполняют поиск и возвращают ноль с одним и тем же Url

class Credit < ActiveRecord::Base
  before_validation :create_code, :if => 'self.new_record?'
  validates :code, :uniqueness => true

  def create_code
    self.code = code_generator
    self.code = code_generator until Credit.find_by_code(code).nil?
  end
end

Если вам абсолютно необходимо удалить случай условия гонки, когда два создания работают в тандеме, и оба запускают поиск с одинаковымcode и return nil, вы можете обернуть находку блокировкой таблицы, для которой требуется специфичный для БД SQL, или вы можете создать таблицу, в которой есть строка, используемая для блокировки с помощью пессимистической блокировки, но я бы не пошел так далеко, если бы вы не ожидали сотнисоздает в секунду, и вам абсолютно необходимо, чтобы пользователь никогда не видел ошибку, это выполнимо, в большинстве случаев просто избыточно.

0 голосов
/ 28 сентября 2011

Я не уверен, есть ли встроенный способ.Я всегда использовал before_create .

Вот пример в контексте UrlShortener.

class UrlShortener < Activerecord::Base
  before_create :create_short_url

  def create_short_url
    self.short_url = RandomString.generate(6)
    until UrlShortener.find_by_short_url(self.short_url).nil?
      self.short_url = RandomString.generate(6)
    end
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...