Ето разликата между уникален индекс и validates_uniqueness_of
Това е корекция, която позволява на ActiveRecord да идентифицира генерирани от db грешки за уникални нарушения на ограниченията. Например, той кара следното да работи, без да декларира validates_uniqueness_of:
create_table "users" do |t|
t.string "email", null: false
end
add_index "users", ["email"], unique: true
class User < ActiveRecord::Base
end
User.create!(email: '[email protected]')
u = User.create(email: '[email protected]')
u.errors[:email]
=> "has already been taken"
Предимствата са бързина, лекота на използване и пълнота --
Скорост
С този подход не е необходимо да правите търсене в db, за да проверите за уникалност при запазване (което понякога може да бъде доста бавно, когато индексът е пропуснат -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Ако наистина ви е грижа за валидирането на уникалността, така или иначе ще трябва да използвате ограничения на базата данни, така че базата данни да потвърди уникалността независимо от всичко и този подход премахва допълнителна заявка. Проверката на индекса два пъти не е проблем за DB (той се кешира втория път), но запазването на DB от приложението е голяма победа.
Лекота на използване
Като се има предвид, че така или иначе трябва да имате db ограничения за истинска уникалност, този подход ще позволи всичко да се случи автоматично, след като db ограниченията са налице. Все още можете да използвате validates_uniqueness_of, ако желаете.
Пълнота
validates_uniqueness_of винаги е бил малко хак -- не може да се справи правилно с условията на състезание и води до изключения, които трябва да се обработват с помощта на донякъде излишна логика за обработка на грешки. (Вижте раздела „Едновременност и интегритет“ в http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )
валидира_уникалността_на не е достатъчно, за да гарантира уникалността на стойността. Причината за това е, че в производството множество работни процеси могат да причинят състезателни условия:
-
Две едновременни заявки се опитват да създадат потребител с едно и също име (и ние искаме потребителските имена да са уникални)
-
Заявките се приемат на сървъра от два работни процеса, които сега ще ги обработват паралелно
-
И двете заявки сканират таблицата с потребители и виждат, че името е налично
-
И двете заявки преминават проверка и създават потребител с привидно налично име
За по-ясно разбиране, моля, проверете това
Ако създадете уникален индекс за колона, това означава, че сте сигурни, че таблицата няма да има повече от един ред със същата стойност за тази колона. Използването само на validates_uniqueness_of validation във вашия модел не е достатъчно за налагане на уникалност, защото може да има едновременни потребители, които се опитват да създадат едни и същи данни.
Представете си, че двама потребители се опитват да регистрират акаунт с един и същ имейл, където сте добавили validates_uniqueness_of :email във вашия потребителски модел. Ако натиснат бутона „Регистрация“ едновременно, Rails ще потърси този имейл в таблицата с потребители и ще отговори, че всичко е наред и че е добре да запазите записа в таблицата. След това Rails ще запише двата записа в потребителската таблица с един и същ имейл и сега имате наистина скапан проблем, с който трябва да се справите.
За да избегнете това, трябва да създадете уникално ограничение и на ниво база данни:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
...
end
add_index :users, :email, unique: true
end
end
Така че чрез създаването на уникалния индекс index_users_on_email получавате две много добри предимства. Цялост на данните и добра производителност, тъй като уникалните индекси обикновено са много бързи.
Ако поставите unique:true в таблицата си с публикации за user_id, това няма да позволи въвеждането на дублиращи се записи със същия user_id.