Überprüfen Sie die Eindeutigkeit mehrerer Spalten


193

Gibt es eine Möglichkeit, zu überprüfen, ob ein tatsächlicher Datensatz eindeutig ist und nicht nur eine Spalte? Beispielsweise sollte ein Freundschaftsmodell / eine Freundschafts-Tabelle nicht mehrere identische Datensätze enthalten können, wie z.

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

7
vergib mir, wenn ich dicht bin, aber wie würde das in dieser Situation helfen?
Re5et

2
Versuchen Sie, "validates_uniqueness_of" in Ihrem Modell zu verwenden. Wenn dies nicht funktioniert, versuchen Sie, einen Index zu erstellen, für den Sie eine Migration von Feilds erstellen können, die eine Anweisung wie add_index: table, [: column_a ,: column_b] ,: unique => true) enthält
Harry Joy

1
@ HarryJoy, fragte er Is there a rails-way way. Und Sie bieten ihm Weg ohne Schienen, aber Standard. The Active Record way claims that intelligence belongs in your models, not in the database.
Grün

2
Leider validates :field_name, unique: trueist es anfällig für Rennbedingungen, so dass eine tatsächliche Einschränkung bevorzugt wird, obwohl dies gegen den Schienenweg erfolgt. @ HarryJoy Ich werde eine Antwort, die den Weg der Einschränkung beschreibt, positiv bewerten.
Pooyan Khosravi

1
Bessere Antwort als alles, was unten angegeben ist, ist dieses eine stackoverflow.com/a/34425284/1612469, da es eine weitere Ebene bringt, um sicherzustellen, dass alles richtig funktioniert
Aleks

Antworten:


319

Sie können einen validates_uniqueness_ofAnruf wie folgt ausführen.

validates_uniqueness_of :user_id, :scope => :friend_id

83
Ich wollte nur hinzufügen, dass Sie mehrere Bereichsparameter übergeben können, falls Sie die Eindeutigkeit in mehr als 2 Feldern überprüfen müssen. Dh: scope => [: friend_id ,: group_id]
Dave Rapin

27
Seltsam, dass man nicht sagen kann validates_uniqueness_of [:user_id, :friend_id]. Vielleicht muss dies gepatcht werden?
Alexey

12
Alexey, validates_uniqueness_of [: user_id ,: friend_id] wird nur die Validierung für jedes der aufgelisteten Felder durchführen - und es ist dokumentiert und erwartetes Verhalten
Nikita Hismatov

71
In Rails 4 wird dies: validiert: user_id, Eindeutigkeit: {scope :: friend_id}
Marina Martin

3
Sie möchten wahrscheinlich eine benutzerdefinierte Fehlermeldung hinzufügen, wie: message => 'hat bereits diesen Freund.'
Laffuste

137

Sie können eine Spalte validatesvalidieren uniqueness:

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

Die Syntax für die Validierung mehrerer Spalten ist ähnlich, Sie sollten jedoch stattdessen ein Array von Feldern angeben:

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

Die oben gezeigten Validierungsansätze haben jedoch eine Rennbedingung und können keine Konsistenz gewährleisten. Betrachten Sie das folgende Beispiel:

  1. Datenbanktabellendatensätze sollen durch n Felder eindeutig sein ;

  2. Mehrere ( zwei oder mehr ) gleichzeitige Anforderungen, die jeweils von separaten Prozessen verarbeitet werden ( Anwendungsserver, Hintergrundarbeitsserver oder was auch immer Sie verwenden ), greifen auf die Datenbank zu, um denselben Datensatz in die Tabelle einzufügen.

  3. Jeder parallele Prozess überprüft, ob ein Datensatz mit denselben n Feldern vorhanden ist.

  4. Die Validierung für jede Anforderung wird erfolgreich bestanden, und jeder Prozess erstellt einen Datensatz in der Tabelle mit denselben Daten.

Um diese Art von Verhalten zu vermeiden, sollte der Datenbank-Tabelle eine eindeutige Einschränkung hinzugefügt werden . Sie können es mit dem add_indexHelfer für ein (oder mehrere) Felder festlegen, indem Sie die folgende Migration ausführen:

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

Vorsichtsmaßnahme : Selbst nachdem Sie eine eindeutige Einschränkung festgelegt haben, versuchen zwei oder mehr gleichzeitige Anforderungen, dieselben Daten in db zu schreiben. Anstatt jedoch doppelte Datensätze zu erstellen, wird eine ActiveRecord::RecordNotUniqueAusnahme ausgelöst, die Sie separat behandeln sollten:

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.