2011-09-03 7 views
11

Supongamos que tenemos la situación:¿Cómo agregar registros existentes a has_many sin guardarlos en la base de datos inmediatamente?

class Pirate < ActiveRecord::Base 
    has_many :parrots 
    validates_presence_of :name 
end 

class Parrot < ActiveRecord::Base 
    belongs_to :pirate 
end 

y tengo Piratas y loros con identificadores de 1 a 10. existente Ahora me gustaría hacer esto:

p = Pirate.first 
p.name = nil 
p.parrot_ids = [1,2,3] 
p.save if p.valid? 

Debido a que el pirata objeto no es válido (falta un nombre) No quiero que se guarde. Sin embargo, los loros están vinculados al pirata ahora y están comprometidos en la base de datos.

¿Cómo puedo asignar los loros, pero los enlaces a los loros solo se guardan en la base de datos cuando p.save tiene éxito? Es decir, ¿cómo puedo guardar el pirata y los enlaces a los loros en una transacción?

Respuesta

1

Se podría reorganizar sus operaciones un poco: "¿si p.valid"

p = Pirate.first 
p.name = nil 
if p.save 
    p.parrot_ids = [1,2,3] 
end 

Nota que no hay necesidad de después de p.save; porque es válido? se invoca mediante guardar, determinando si se intenta escribir los datos en la base de datos.

Si sus loros no eran preexistentes, podría usar p.parrots.build (attributes = {...}) para crear nuevos loros que no se guardarán hasta que se guarde el padre pirata.

Consulte la sección sobre objetos no guardados y asociaciones en el ActiveRecord::Associations::ClassMethods documentation.

+0

Hola Ken, quiero que los loros participen en la validación. Tengo una asignación masiva en: parrot_ids. Si la validación falla porque el usuario intentó hackear un loro al que no puede enlazar, entonces p.save debería fallar, después de lo cual p.parrots debería devolver los loros que el usuario intentó. –

1

Desafortunadamente, Rails es mucho construidos de esta manera para los objetos existentes; si el objeto ya existe y usted usa sus asociaciones, siempre se activa una actualización. Si tiene la capacidad de utilizar la opción .build como mencionó KenB, esa es una forma de evitar el problema. Sin embargo, salvo eso, solo puedo pensar en una forma de manejar esto en este momento; envuelva toda la operación en una transacción, así:

Pirate.transaction do 
    p = Pirate.first 
    p.name = nil 
    p.parrot_ids = [1,2,3] 

    if !p.save # Performing save in this manner will return false if validations fail (ie same as your p.valid?) 
     raise ActiveRecord::Rollback # should rollback anything executing within this transaction block 
    end 
end 

Déjame saber si esto ayuda, y disculpas si no lo hace.

+0

Por alguna razón acabo de notar que esta pregunta tiene más de dos años. Perdón por la nigromancia de la pregunta (suponiendo que ya no es relevante). –

Cuestiones relacionadas