2012-07-27 12 views
6

Tengo un formulario de inscripción.Rails 3: rollback for after_create

Cuando el usuario se inscribe, la aplicación debe guardar los datos en la tabla enrollments y en la tabla users. (Necesito esta separación porque el perfil del usuario puede cambiar pero los datos que ingresó para esa inscripción en particular deben ser archivados. De modo que aunque más tarde el usuario cambie su apellido, en el formulario de inscripción tendré su información inicial)

así que estaba pensando acerca de cómo guardar los datos en la tabla enrollments luego tener una llamada after_create, así ...

class Enrollment < ActiveRecord::Base 

    after_create :save_corresponding_user 

    def save_corresponding_user 
    user = User.new 
    user.full_name = self.user_full_name 
    user.email = self.user_email 
    user.mobile_phone = self.user_mobile_phone 
    user.save 
    end 
end 

la cuestión es, ¿y si ahorrando al usuario falla por cualquier razón. ¿Cómo puedo deshacer y destruir los datos recién guardados de la tabla enrollments?

+0

Se puede inscribir a un usuario varias veces? Si no, simplemente agregaría los campos adicionales directamente en la tabla de usuarios. –

+0

Bueno, en realidad, es un padre/tutor que matricula a un niño en una guardería. Entonces sí, el padre puede inscribir a un niño varias veces. – leonel

+0

Para mí, parece que poner save_corresponding_user en after_create será un problema. ¿Qué sucede cuando el usuario se inscribe nuevamente? No querrá crear un nuevo objeto de usuario para ellos. Además, ¿no es probable que la tabla de inscripciones tenga una columna user_id? Eso no estaría poblado en su código anterior. Si fuera yo, simplemente envolvería la creación de la inscripción y el usuario en una transacción, que manejará el reinicio de ambos en caso de error. –

Respuesta

12

after_create es una parte de la transacción que guarda el modelo actual. Por lo tanto, si el código falla o si after_create devuelve false, debe deshacer la transacción actual e invalidar el guardado enrollment.

Si desea simular esto, agregar esto a su after_create y ver si todo funciona como se esperaba:

raise Exception.new("CRASH") 
+0

la reversión no ocurre si está utilizando una base de datos no transaccional como mongodb –

+3

Si after_create devuelve falso no parece deshacer la transacción tampoco (solo la excepción). Al menos en Rails 4. – djburdick

+0

@djburdick Una transacción por defecto solo debería retrotraerse si se produce una excepción. Entonces este es el comportamiento esperado. Creo que esta respuesta es un poco incorrecta con respecto a ese detalle. – leishman

1

Como se mencionó @anthonyalberto, after_create ya es parte de la transacción. Para definir una transacción que usaría algo como esto en su controlador:

Enrollment.transaction do 
    @enrollment.save! 
end 

Eso es realmente todo lo que necesita hacer, si el salvamento de la inscripción falla o el salvamento de usuario no se deshará toda la transacción. Aquí hay más información: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html

7

De vuelta false de after_create no hará nada.

Toda la cadena de devolución de llamada está envuelta en una transacción. Si cualquier método de devolución de llamada anterior devuelve exactamente falso o genera una excepción, la cadena de ejecución se detiene y se emite un ROLLBACK; después de las devoluciones de llamada solo puede lograr eso al plantear una excepción.

Además, debe raise ActiveRecord::Rollback:

Cualquier excepción que no es Rollback ActiveRecord :: será re-levantado por los carriles después de la cadena de devolución de llamada se detiene. Plantear una excepción que no sea ActiveRecord :: Rollback puede romper código que no espera métodos como guardar y atributos_actualizar (que normalmente intentan devolver verdadero o falso) para generar una excepción.

http://guides.rubyonrails.org/active_record_callbacks.html#halting-execution

hago algo como esto:

after_create do 
    if condition 
    errors.add(:attr, 'Blah blah blah.') 
    raise ActiveRecord::Rollback 
    end 
end 

para los carriles 3: http://guides.rubyonrails.org/v3.2.13/active_record_validations_callbacks.html#halting-execution