2011-05-12 17 views
21

Estoy escribiendo una forma de asistente en rieles; p.ej. múltiples páginas de entrada para un objeto modelo.¿Cómo validar un objeto de modelo anidado basado en el estado del objeto primario?

Los fundamentos de mi enfoque son los descritos en Ryan Bates 'Multistep form railscast: http://railscasts.com/episodes/217-multistep-forms (en caso de que alguien quiera saber el POR QUÉ detrás de algunos de los códigos a continuación).

Los objetos bajo escrutinio aquí son "Participante", que tiene una "dirección"

Mi problema es que yo sólo quiero validar el objeto anidado (Dirección) cuando el usuario está tratando de progresar más allá de la entrada de direcciones pantalla. Esto es seguido actualmente a través de un atributo en el modelo de participante llamado "CURRENT_STEP"

Así que tienen un Participante:

class Participant < ActiveRecord::Base 
    has_one :address 
    accepts_nested_attributes_for :address 
    validates_presence_of :first_name, :last_name, :if => self.current_step == "name" 
    ... 
    def steps = %w[ name address confirm ] # the steps the wizard will follow 
end 

y una dirección:

class Address < ActiveRecord::Base 
    belongs_to :participant 
    validates_presence_of :address1, :state, :suburb, :postcode #, :if => participant.current_step == "address" 
end 

El principio de este método es el La acción "crear" se invoca en el controlador (no se muestra) para cada paso del asistente, y solo valida un subconjunto del modelo cuando se procesa cada paso.

Actualmente, cuando completo la primera pantalla ("nombre") y trato de ir al paso de la dirección, la validación de la dirección se activa y me devuelven a la pantalla "nombre" con errores de validación de la dirección en blanco detalles.

Así que he intentado una serie de enfoques aquí, la parte final de la cual fue la condición comentada en validación de dirección que se muestra arriba - esto he encontrado que no funciona ya que solo estoy construyendo participantes-> objetos de dirección , pero no salvarlos. Por lo tanto, @participant.address me da el objeto de dirección, pero @participant.address.participant es nulo, ya que la dirección aún no tiene una clave externa participante_id para buscarla.

El motivo de mis problemas parece ser la inclusión del super-práctico accepts_nested_attributes_for método. Esperaba usar un validates_associated para hacer la validación, pero veo que la etiqueta accepts_nested_attributes_for propaga parámetros de forma muy bien para crear objetos de modelo anidados, pero también asegura que el método participant#valid? llama a la validación de direcciones en TODAS las situaciones.

Entonces mi dilema es cómo usar mejor el método participant#valid? para validar el modelo parcialmente completo, basado en el parámetro current_step en el participante?

EDITAR - actualizado para eliminar información adicional, y destilar hasta el problema central

+0

Solo una conjetura, pero ha intentado valida: address,: if => Proc.new {| participante | participante.current_step == 'nombre'} – dogenpunk

+0

Una de las primeras cosas que intenté, pero como se menciona arriba (quizás no demasiado claro, lo admito) el objeto de dirección no tiene participante hasta que se guarda, por lo que recibí un error al llamar a current_step una clase nula – Phantomwhale

+0

Agregue: inverso de sus relaciones, por lo que el objeto de dirección tendrá una referencia a su participante cuando se construya. – graywh

Respuesta

18

Añadir un atributo virtual en su Address modelo:

class Address < ActiveRecord::Base 
    belongs_to :participant 
    attr_accessor :skip_validation 
    validates_presence_of :address1, :state, :suburb, :postcode, 
          :unless => :skip_validation 
end 

establece el atributo virtual en el objeto de dirección cuando current_step se establece.

class Participant < ActiveRecord::Base 
    has_one :address 
    accepts_nested_attributes_for :address 
    attr_accessor :current_step 
    validates_presence_of :first_name, :last_name, 
         :if => lambda {|r| r.current_step == "name"} 


    def current_step=(value) 
    unless (value == "address") 
     address.skip_validation = true 
    end  
    @current_step = value 
    end  
end 
+1

Buena idea. Estoy llamando al atributo 'skip_validation' y uso un': unless' en lugar de ': if', por lo que el valor predeterminado es validar. Funciona muy bien hasta ahora. – Thilo

+0

Bueno, eliminé mi asistente de rails hace varios meses, y desde entonces he abordado más problemas de modelos anidados, pero es genial volver a esto y ver lo que parece ser una respuesta sólida al problema. Gracias. – Phantomwhale

+0

¡Esto era oro! ¡Gracias! –

0

¿Qué tal asignación en el controlador antes de llamar participant.valid?:

@participant.address.participant = @participant 
0

Aunque el ActiveRecord "validar" los métodos son muy convenientes, no hay nada que le impida "ruede su propio", tal vez usando before_save ganchos. Puede reemplazar su validador validates_presence_of en Address con un gancho before_save que solo valida bajo ciertas condiciones. Entonces accepts_nested_attributes_for presumiblemente no lo vería.

Cuestiones relacionadas