2013-04-23 5 views
5

Ich habe seit einigen Stunden zu kämpfen, um Validierungen verschachtelter Attribute in meiner Rails-App arbeiten zu lassen. Ein kleiner Vorbehalt ist, dass ich geschachtelte Attribute dynamisch basierend auf den Attributen ihrer Eltern validieren muss, da sich die Menge der benötigten Informationen im Laufe der Zeit ändert, je nachdem, wo sich der Elternteil befindet.Rails accepts_nested_attributes_for validation auf transactional Objekt

Also hier ist mein Setup: Ich habe ein Elternteil mit vielen verschiedenen verknüpften Modellen und ich möchte nachgeschachtelte Attribute dieser jedes Mal validieren, wenn ich das Elternteil speichern. In Anbetracht der Tatsache, dass Validierungen dynamisch ändern, ich hatte eine benutzerdefinierte Validierungsmethode in dem Modell zu schreiben:

class Parent < ActiveRecord::Base 
    attr_accessible :children_attributes, :status 
    has_many :children 
    accepts_nested_attributes_for :children 
    validate :validate_nested_attributes 
    def validate_nested_attributes 
    children.each do |child| 
     child.descriptions.each do |description| 
     errors.add(:base, "Child description value cant be blank") if description.value.blank? && parent.status == 'validate_children' 
     end 
    end 
    end 
end 


class Child < ActiveRecord::Base 
    attr_accessible :descriptions_attributes, :status 
    has_many :descriptions 
    belongs_to :parent 
    accepts_nested_attributes_for :descriptions 
end 

In meinem Controller ich update_attributes an der Eltern rufen, wenn ich speichern möchten. Das Problem ist nun, dass rails anscheinend die Validierungen für die Datenbank ausführt und nicht für das Objekt, das vom Benutzer oder vom Controller geändert wurde. Was also passieren kann, ist, dass der Wert eines Kindes von einem Benutzer gelöscht wird und die Validierungen bestehen bleiben, während spätere Validierungen nicht bestanden werden, weil das Element in der Datenbank nicht gültig ist.

Hier ist ein kurzes Beispiel für dieses Szenario:

parent = Parent.create({:status => 'validate_children', :children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}}) 
    #true 
parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => nil}}}}) 
    #true!!/since child.value.blank? reads the database and returns false 
parent.update_attributes({:children_attributes => {0 => {:descriptions_attributes => { 0 => {:value => 'Not blank!'}}}}) 
    #false, same reason as above 

Die Validierung für First-Level-Verbände arbeitet, zum Beispiel Wenn ein Kind ein Attribut 'value' hat, könnte ich eine Validierung so durchführen, wie ich es tue. Das Problem liegt in tiefen Assoziationen, die vor dem Speichern offensichtlich nicht validiert werden können.

Kann mir jemand in die richtige Richtung zeigen, wie man das löst? Die einzige Möglichkeit, die ich derzeit sehe, ist das Speichern von Datensätzen, die anschließende Validierung und das Löschen/Zurücksetzen von Daten, wenn die Validierung fehlschlägt, aber ich hoffe ehrlich auf etwas Saubereres.

Vielen Dank im Voraus!

SOLUTION

So stellt sich heraus, dass ich durch Bezugnahme auf die direkt in der benutzerdefinierten Validierung, auf diese Weise Validierungen auf tief verschachtelten Modellen ausgeführt wird:

class Parent < ActiveRecord::Base 
    [...] 
    has_many :descriptions, :through => :children 
    [...] 
    def validate_nested_attributes 
    descriptions.each do |description| 
     [...] 
    end 
    end 
end 

die aus irgendeinem Grund führt zu den Problemen, Ich hatte oben. Danke Santosh, dass ich meinen Beispielcode getestet und berichtet habe, dass es funktioniert, das hat mich in die richtige Richtung gelenkt, um das herauszufinden.

Für zukünftige Referenz funktioniert der Code in der ursprünglichen Frage für diese Art von dynamischen, tief verschachtelten Validierungen.

+0

Es gibt einige Fehler in Ihrem Code verwenden sollten. Es könnte Tippfehler sein. 1. "Kindwert kann nicht leer sein" - einzelnes Zitat innerhalb des einzelnen Anführungsstrichs. 2. && parent.status = 'validate_children' - Dies ist kein Vergleich. seine Zuordnung –

+0

Entschuldigung wegen der Tippfehler! Der Code hier ist nur ein vereinfachtes Beispiel meiner App, so dass es nicht der Grund ist, warum es nicht funktioniert, aber danke, dass du das herausgebracht hast :) –

Antwort

2

Ich glaube, Sie validates_associated für dieses

mit dem Anschluss an die Validierung in der Kinder

validates :value, :presence => true, :if => "self.parent.status == 'validate_children'" 
+0

Hallo Santhosh, danke für die sehr schnelle Antwort! Ich bin bereits in validates_associated eingetreten, aber das allgemeine Gefühl hier ist, dass es mehr ein Streit als alles andere ist. Gibt es eine Möglichkeit, verknüpfte Datensatzattribute nur aus dem Modell des übergeordneten Elements zu validieren, ohne ihr eigenes Modell zu berühren?Dies ist erforderlich, da das Kind in meiner Datenbank ohne Wert in der Datenbank leben kann, jedoch nur, wenn der Status des Elternelements von "validate_children" abweicht. –

+0

In diesem Fall können Sie die Validierung in Kindern wie folgt hinzufügen: validates: value,: present => true,: if => "self.parent.status == 'validate_children'" –

+0

Hey Santhosh, nochmals vielen Dank für die Antwort. Das Problem ist, dass meine App ein bisschen komplizierter ist als das Beispiel und es beinhaltet auch polymorphe Assoziationen, daher wäre es viel besser, Validierungen auf der übergeordneten Ebene zu behalten. Ich habe gerade herausgefunden, dass die Validierungen auf verschachtelten Modellen der ersten Ebene funktionieren, aber nicht auf tief verschachtelten. Z.B. Wenn Kinder Beschreibungen haben, funktioniert die Validierung für Kinder, aber nicht für ihre Beschreibungen. –

Verwandte Themen