Convalida l'unicità di più colonne


193

Esiste un modo rails per convalidare che un record effettivo è unico e non solo una colonna? Ad esempio, un modello / tabella di amicizia non dovrebbe essere in grado di avere più record identici come:

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20

7
perdonami se sono denso, ma come sarebbe d'aiuto in questa situazione?
re5et

2
prova a utilizzare "validates_uniqueness_of" nel tuo modello. se questo non funziona, prova a creare un indice su cui è possibile creare una migrazione di feilds che includa un'istruzione come add_index: table, [: column_a,: column_b],: unique => true)
Harry Joy

1
@HarryJoy, ha chiesto Is there a rails-way way. E gli offri un modo non rotaie, ma standard. The Active Record way claims that intelligence belongs in your models, not in the database.
Green

2
Sfortunatamente validates :field_name, unique: trueè incline alle condizioni di gara, quindi anche se contro le rotaie, è preferibile un vero vincolo. @HarryJoy Valuterò una risposta che descrive la via del vincolo.
Pooyan Khosravi,

1
risposta migliore quindi tutto quanto indicato di seguito è questo stackoverflow.com/a/34425284/1612469 poiché porta un altro livello per assicurarsi che tutto funzioni correttamente
Aleks

Risposte:


319

È possibile ambito una validates_uniqueness_ofchiamata come segue.

validates_uniqueness_of :user_id, :scope => :friend_id

83
Volevo solo aggiungere che è possibile passare più parametri dell'ambito nel caso in cui sia necessario convalidare l'unicità su più di 2 campi. Vale a dire: scope => [: friend_id,: group_id]
Dave Rapin

27
Strano che non si possa dire validates_uniqueness_of [:user_id, :friend_id]. Forse questo deve essere patchato?
Alexey,

12
Alexey, validates_uniqueness_of [: user_id,: friend_id] eseguirà solo la convalida per ciascuno dei campi elencati - ed è un comportamento documentato e atteso
Nikita Hismatov

71
In Rails 4, questo diventa: convalida: user_id, unicità: {scope:: friend_id}
Marina Martin

3
Probabilmente vuoi aggiungere un messaggio di errore personalizzato come: message => 'ha già questo amico.'
Laffuste,

137

È possibile utilizzare validatesper convalidare uniquenesssu una colonna:

validates :user_id, uniqueness: {scope: :friend_id}

La sintassi per la convalida su più colonne è simile, ma è necessario fornire invece una matrice di campi:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}

Tuttavia , gli approcci di validazione mostrati sopra hanno una condizione di competizione e non possono garantire coerenza. Considera il seguente esempio:

  1. i record della tabella del database dovrebbero essere univoci per n campi;

  2. più ( due o più ) richieste simultanee, gestite da processi separati ciascuna ( server applicazioni, server di background o qualunque cosa tu stia utilizzando ), accedono al database per inserire lo stesso record nella tabella;

  3. ogni processo in parallelo convalida se esiste un record con gli stessi n campi;

  4. la convalida per ogni richiesta viene passata correttamente e ogni processo crea un record nella tabella con gli stessi dati.

Per evitare questo tipo di comportamento, si dovrebbe aggiungere un vincolo univoco alla tabella db. Puoi impostarlo con l' add_indexhelper per uno (o più) campi (s) eseguendo la seguente migrazione:

class AddUniqueConstraints < ActiveRecord::Migration
  def change
   add_index :table_name, [:field1, ... , :fieldn], unique: true
  end
end

Avvertenza : anche dopo aver impostato un vincolo univoco, due o più richieste simultanee proveranno a scrivere gli stessi dati su db, ma invece di creare record duplicati, ciò solleverà ActiveRecord::RecordNotUniqueun'eccezione, che dovresti gestire separatamente:

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.