2016-07-19 22 views
0

Angenommen, Sie haben User has_many Books. Jedes Buch hat ein Namensfeld.Wie kann die Eindeutigkeit eines Feldes validiert werden, ohne Rails zu speichern?

Der Benutzer gibt seine Bücher ein und es wird der App als ein Array von Namen übermittelt. Das Array von Namen ersetzt alle vorhandenen Bücher.

Wenn die Aktualisierung fehlschlägt, sollten die Bücher nicht geändert werden.

class Book 
    belongs_to :user 

    validates_uniquness_of :name, scope: [:user] 

Wie überprüft man die Gültigkeit jedes Buches ohne zu speichern?

Zum Beispiel:

['Rails Guide', 'Javascript for Dummies'] würde gültig sein.

['Javascript for Dummies', 'Javascript for Dummies'] wäre nicht gültig.

params[:books].each{| b | Book.new(b).valid? } wird nicht funktionieren, weil das Buch gespeichert werden muss, um die Einzigartigkeit zu erhalten.

Mongoid

Antwort

1

können Sie einen Active Record Transaction verwenden. Starten Sie die Transaktion, rufen Sie save, und wenn es fehlschlägt, wird die gesamte Transaktion zurückgesetzt. Beispiel:

Book.transaction do 
    params[:books].each{ |b| Book.new(b).save! } 
end 

Die gesamte Transaktion wird abgebrochen, wenn eine Ausnahme vorliegt. Sie sollten diesen Fall behandeln, indem Sie ActiveRecord::RecordInvalid fangen.

+0

Sie benötigen auch eine Eindeutigkeitseinschränkung in der Datenbank und dann müssen Sie auch 'ActiveRecord :: RecordNotUnique' abfangen. –

+0

Dies ist ein guter Weg, es zu tun, aber Mongo hat keine Transaktionen. –

1

Sie können Array#map verwenden, um das Array von Buchattributen in ein Array von Buchnamen umzuwandeln. Dann Array#uniq verwenden Duplikate entfernen aus dem Array von Bücher-Namen und dann prüfen, ob resultierenden Array die gleiche Größe hat wie die ursprüngliche Anordnung der Bücher Attribute:

are_books_uniq = params[:books].map{|b| b[:name]}.uniq.size == params[:books].size 

So können Sie Ihre Prüfung durchführen können, ohne die Datenbank zu berühren. Aber um auf der sicheren Seite zu sein, sollten Sie alle Bücher innerhalb einer Transaktion speichern (siehe @ Aarons answer).

+0

Ich mochte diese Vorgehensweise nicht, weil sie die Validierungslogik dupliziert. –

1

Dies erwies sich als viel komplizierter als ich mir vorgestellt habe.

Die Lösung, die ich mit Blicken kam wie:

def update params 
    names = params.delete(:books) 

    new_books = names.map{| title | Book.new(name:name)} 
    validate_books_for new_books 

    return false if errors.present? 
    return false unless super(params) 

    self.books = new_books 

    self 
    end 

Die meisten der Komplexität kommt von der Kopplung der zwei Modelle. Ich kann sehen, warum es keine gute Idee ist, Modelle zu koppeln. Vielleicht wäre ein besseres Design, die Bücher als ein Array zu speichern.