validates_uniqueness_of and MySQL unique index

Oct 11
2006 09:52 (Программирование, Ruby on Rails) · English (8,888 views)

Вчера в русскоязычной рассылке по RoR обсуждалась следующая проблема. MySQL поддерживает уникальные индексы, а модели - ограничение validates_uniqueness_of. Нужно ли нам обрабатывать исключения MySQL, или валидации RoR достаточно?

Я считаю, что обрабатывать нужно, но для убедительности решил провести тест. Для начала я создал таблицу с уникальным индексом:

class CreateCustomers < ActiveRecord::Migration
  def self.up
    create_table :customers do |t|
      t.column :name, :string
    end
    add_index :customers, :name, :unique => true
  end

  def self.down
    drop_table :customers
  end
end

Затем я описал следующую модель:

class Customer < ActiveRecord::Base
  validates_presence_of :name, :if => lambda { |customer|
    unless @@created
      @@created = true
      Customer.create(:name => customer.name)
    end
    true
  }
  validates_uniqueness_of :name
 
  @@created = false
end

Вы видите два правила валидации: первое это проверка уникальности, наша основная цель. Задачей второго правила является создание второго объекта модели между валидацией и вставкой в базу первого объекта. Правила валидации отрабатывают в обратном порядке, потому validates_presence_of идет первым. Осталось запустить script/console и набрать:

Customer.create(:name => 'name')

Вы можете найти лог ниже. Думаю, комментарии излишни.

SHOW FIELDS FROM customers BEGIN SELECT * FROM customers WHERE (customers.name = 'name') LIMIT 1 SELECT * FROM customers WHERE (customers.name = 'name') LIMIT 1 INSERT INTO customers (`name`) VALUES('name') Mysql::Error: #23000Duplicate entry 'name' for key 2: INSERT INTO customers (`name`) VALUES('name') ROLLBACK

Примечание: Вы правы, это неправильная логика, но я использовал ее только для того, чтобы показать, что может произойти, если два процесса одновременно добавят запись с одинаковым полем name. Вы можете заменить код внутри :if на sleep 10 и запустить две консоли, чтобы воспроизвести исключение.

Оставить отзыв

Вы можете использовать простые теги форматирования HTML (вроде <a>, <ul> and others). Чтобы вставить пример код, используйте <code lang="php">$a = "hello";</code> (поддерживаемые языки: ruby, php, yaml, html, csharp, javascript). Также Вы можете использовать <code>$a = "hello";</code>, синтаксис не будет подсвечен. Если вы не хотите использовать тег <code>, замените символ < на &lt;.

Отправить

 
Copyright © 2005 - 2008, Dmytro Shteflyuk