2009-07-14 10 views
0

Ich habe ein Entry Modell, das viele Tag s hat. Tag s werden zu einem Eintrag hinzugefügt, indem Sie sie über ein virtuelles Attribut tag_names in ein Textfeld in meinem Formular eingeben. Vor der Validierung für das Modell Entry wird die Zeichenfolge tag_names unter Verwendung von find_or_create_by_name in tatsächliche Tag Objekte konvertiert. Das Modell Tag verfügt außerdem über eine Validierung, um sicherzustellen, dass der Tag-Name einer Regex entspricht, die über die Assoziation ausgeführt wird.ActiveRecord Zuordnung wird vor dem Datensatz in einem Update gespeichert

Mein Einstiegsmodell sieht wie folgt aus:

class Entry < ActiveRecord::Base 
    has_many :entry_tags 
    has_many :tags, :through => :entry_tags 

    before_validation :update_tags 

    attr_writer :tag_names 

private 
    def update_tags 
    if @tag_names 
     self.tags = @tag_names.split(",").uniq.map do |name| 
     Tag.find_or_create_by_name(name.strip) 
     end 
    end 
    end 
end 

Wenn ich ein neues Entry-Objekt erstellen und zuweisen Tags, alles funktioniert einwandfrei - die Tags werden nicht gespeichert, wenn es ein Fehler Validierung ist auf einem der Tag s, und eine Fehlermeldung wird zurückgegeben. Wenn ich jedoch versuche, ein vorhandenes Entry-Objekt mit einem ungültigen Tag zu aktualisieren, statt eine Nachricht zurückzugeben, löst mein Aufruf self.tags= (in update_tags oben) eine Ausnahme mit der Validierungsfehlermeldung aus. Selbst wenn ich find_or_create_by_name überschreibe, um nur ein neues Objekt zurückzugeben, anstatt create aufzurufen, bekomme ich das gleiche Ergebnis.

Es scheint mir (und die docs scheinen zu bestätigen), dass die tags= Anruf tatsächlich meine Tag Objekte speichert, bevor der Haupt-Datensatz gespeichert wird, wenn die Entry Objekt bereits vorhanden ist. Gibt es etwas, was ich tun kann, damit das Speichern nicht geschieht, oder um zu verhindern, dass es eine Ausnahme auslöst und nur bewirkt, dass meine Speicherung false zurückgibt?

Antwort

2

würde ich so etwas wie dies versuchen:

class Entry < ActiveRecord::Base 
    has_many :entry_tags 
    has_many :tags, :through => :entry_tags 

    before_validation :update_tags 

    attr_writer :tag_names 
    validates_associated :tags 

private 
    def update_tags 
    return unless @tag_names 
    current_tag_names = tags.map(&:name) 
    user_tag_names = @tag_names.split(",").uniq 
    #add in new tags 
    user_tag_names.each do |name| 
     next if current_tag_names.include?(name) 
     tags.build :name => name 
    end 
    #remove dropped tags 
    (current_tag_names - user_tag_names).each do |name| 
     removed_tag = tags.find_by_name(name) 
     tags.delete(removed_tag) 
    end 
    end 
end 

Auf diese Weise können nur sind die entsprechenden Modelle in Ihrer update_tags Aktion Initialisierung und so wird nicht Validierungsfehler werfen. Ich fügte auch in validates_associated :tags hinzu, so dass Fehler in diesen verwandten Modellen über das Standardeingabeformular unter Verwendung error_messages_for :entry zurückgemeldet werden können.

Update enthaltener Code zum Entfernen von gelöschten Tags.

+0

Würde das nicht einfach an Tags angehängt anstatt überschrieben? Ich denke, ich müsste etwas wie "tags.clear" in meiner 'update_tags' Methode machen? Außerdem habe ich versucht, validates_associated vorher zu verwenden, aber ich war am Ende mit doppelten Fehlermeldungen für Tags. –

+0

hmm, ich würde kein "tags.clear" machen - ich bearbeite meinen Code, um mit dem Entfernen von Tags umzugehen, die der Benutzer gelöscht hat. Sie können glücklicher sein, Ihre eigene benutzerdefinierte Validierungsfunktion für das Tags-Array zu schreiben. Vielleicht lohnt es sich, eine weitere Frage zu diesem Thema zu stellen. – austinfromboston

1

Sie könnten die Ausnahme ausgelöst und false zurückgeben, in diesem Fall von update_tags, die das Speichern auf dem Entry stoppen wird. Wenn Sie vermeiden möchten, dass diese Ausnahme behandelt wird, können Sie alternativ eine neue Tag-Instanz erstellen, in der noch nicht vorhanden ist, und prüfen, ob sie gültig ist, bevor Sie fortfahren (new_tag.valid?). Wenn sie nicht zurückgegeben wird, geben Sie false von update_tags zurück .

Verwandte Themen