Hier is het verschil tussen unieke index en valids_uniqueness_of
Dit is een patch om ActiveRecord in staat te stellen db-gegenereerde fouten te identificeren voor unieke beperkingsschendingen. Het zorgt er bijvoorbeeld voor dat het volgende werkt zonder een Valids_uniqueness_of te declareren:
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"
De voordelen zijn snelheid, gebruiksgemak en volledigheid --
Snelheid
Met deze aanpak hoeft u geen db-zoekopdracht uit te voeren om te controleren op uniciteit bij het opslaan (wat soms vrij traag kan zijn als de index wordt gemist -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Als je echt geïnteresseerd bent in het valideren van uniciteit, zul je toch databasebeperkingen moeten gebruiken, zodat de database de uniciteit zal valideren, wat er ook gebeurt, en deze aanpak verwijdert een extra query. Twee keer de index controleren is geen probleem voor de DB (de 2e keer wordt deze in de cache opgeslagen), maar het opslaan van een DB-retour vanuit de applicatie is een grote overwinning.
Gebruiksgemak
Aangezien je hoe dan ook db-beperkingen moet hebben voor echte uniciteit, laat deze benadering alles gewoon automatisch gebeuren zodra de db-beperkingen op hun plaats zijn. Je kunt nog steeds valids_uniqueness_of gebruiken als je dat wilt.
Volledigheid
valids_uniqueness_of is altijd een beetje een hack geweest -- het kan race-omstandigheden niet goed aan en resulteert in uitzonderingen die moeten worden afgehandeld met behulp van enigszins overbodige logica voor foutafhandeling. (Zie het gedeelte "Gelijktijdigheid en integriteit" in http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )
validates_uniqueness_of is niet voldoende om de uniciteit van een waarde te waarborgen. De reden hiervoor is dat in de productie meerdere arbeidsprocessen rassenomstandigheden kunnen veroorzaken:
-
Twee gelijktijdige verzoeken proberen een gebruiker met dezelfde naam te maken (en we willen dat gebruikersnamen uniek zijn)
-
De verzoeken worden op de server geaccepteerd door twee werkprocessen die ze nu parallel zullen verwerken
-
Beide verzoeken scannen de gebruikerstabel en zien dat de naam beschikbaar is
-
Beide verzoeken slagen voor validatie en maken een gebruiker aan met de schijnbaar beschikbare naam
Controleer dit voor meer duidelijkheid
Als u een unieke index voor een kolom maakt, betekent dit dat u er zeker van bent dat de tabel niet meer dan één rij met dezelfde waarde voor die kolom bevat. Het gebruik van alleen Valids_uniqueness_of Validation in uw model is niet voldoende om uniciteit af te dwingen, omdat er gelijktijdige gebruikers kunnen zijn die dezelfde gegevens proberen te creëren.
Stel je voor dat twee gebruikers een account proberen te registreren met hetzelfde e-mailadres waar je valids_uniqueness_of :email in je gebruikersmodel hebt toegevoegd. Als ze tegelijkertijd op de knop "Aanmelden" klikken, zal Rails in de gebruikerstabel naar die e-mail zoeken en antwoorden dat alles in orde is en dat het goed is om het record in de tabel op te slaan. Rails zal dan de twee records met dezelfde e-mail in de gebruikerstabel opslaan en nu heb je een heel vervelend probleem om op te lossen.
Om dit te voorkomen, moet u ook een unieke beperking op databaseniveau maken:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
...
end
add_index :users, :email, unique: true
end
end
Dus door de index_users_on_email unieke index te maken, krijgt u twee zeer mooie voordelen. Gegevensintegriteit en goede prestaties omdat unieke indexen vaak erg snel zijn.
Als je unique:true in je berichtentabel voor user_id zet, dan staat het niet toe om dubbele records in te voeren met dezelfde user_id.