sql >> Databasteknik >  >> RDS >> PostgreSQL

Rails vad som är skillnaden i unikt index och validates_uniqueness_of

Här är skillnaden mellan unikt index och validates_uniqueness_of

Detta är en patch för att aktivera ActiveRecord att identifiera db-genererade fel för unika begränsningsöverträdelser. Till exempel får det följande att fungera utan att deklarera en 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"

Fördelarna är snabbhet, användarvänlighet och fullständighet --

Hastighet

Med det här tillvägagångssättet behöver du inte göra en db-uppslagning för att kontrollera om det är unikt när du sparar (vilket ibland kan vara ganska långsamt när indexet missas -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Om du verkligen bryr dig om att validera unikhet måste du ändå använda databasbegränsningar så databasen kommer att validera unikhet oavsett vad och detta tillvägagångssätt tar bort en extra fråga. Att kontrollera indexet två gånger är inte ett problem för DB (den cachelagras andra gången), men att spara en DB tur och retur från applikationen är en stor vinst.

Användarvänlighet

Med tanke på att du måste ha db-begränsningar för sann unikhet ändå, kommer detta tillvägagångssätt att låta allt bara hända automatiskt när db-begränsningarna är på plats. Du kan fortfarande använda validates_uniqueness_of om du vill.

Fullständighet

validates_uniqueness_of har alltid varit lite av ett hack -- det kan inte hantera tävlingsförhållanden ordentligt och resulterar i undantag som måste hanteras med något redundant felhanteringslogik. (Se avsnittet "Samtidighet och integritet" i http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )

validerar_uniqueness_of är inte tillräckligt för att säkerställa ett värdes unika karaktär. Anledningen till detta är att i produktionen kan flera arbetsprocesser orsaka rasförhållanden:

  1. Två samtidiga förfrågningar försöker skapa en användare med samma namn (och vi vill att användarnamn ska vara unika)

  2. Förfrågningarna accepteras på servern av två arbetsprocesser som nu kommer att behandla dem parallellt

  3. Båda förfrågningarna skannar användartabellen och ser att namnet är tillgängligt

  4. Båda förfrågningarna klarar validering och skapar en användare med det till synes tillgängliga namnet

Kontrollera detta för en tydligare förståelse

Om du skapar ett unikt index för en kolumn betyder det att du är garanterad att tabellen inte kommer att ha mer än en rad med samma värde för den kolumnen. Att endast använda validates_uniqueness_of validation i din modell är inte tillräckligt för att framtvinga unikhet eftersom det kan finnas samtidiga användare som försöker skapa samma data.

Föreställ dig att två användare försöker registrera ett konto med samma e-postadress där du har lagt till validates_uniqueness_of :email i din användarmodell. Om de trycker på knappen "Registrera dig" samtidigt, kommer Rails att leta efter det e-postmeddelandet i användartabellen och svara att allt är bra och att det är ok att spara posten i tabellen. Rails kommer sedan att spara de två posterna i användartabellen med samma e-post och nu har du ett riktigt taskigt problem att ta itu med.

För att undvika detta måste du skapa en unik begränsning på databasnivå också:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email
      ...
    end
    
    add_index :users, :email, unique: true
  end
end

Så genom att skapa index_users_on_email unika index får du två mycket trevliga fördelar. Dataintegritet och bra prestanda eftersom unika index tenderar att vara mycket snabba.

Om du anger unikt:true i din posttabell för user_id kommer det inte att tillåta att ange dubbletter av poster med samma user_id.



  1. Det går inte att ansluta Ruby on Rails till mysql-fjärrdatabas

  2. Automatisk anslutning till PDO endast vid behov

  3. Mysql :Inte tillåtet att returnera en resultatuppsättning från en funktion

  4. Hur man får tillbaka *allt* från en lagrad procedur med JDBC