Rails update_attributes sin guardar?

386

¿Existe una alternativa a update_attributes que no guarde el registro?

Entonces podría hacer algo como:

@car = Car.new(:make => 'GMC')
#other processing
@car.update_attributes(:model => 'Sierra', :year => "2012", :looks => "Super Sexy, wanna make love to it")
#other processing
@car.save

Por cierto, sé que puedo @car.model = 'Sierra', pero quiero actualizarlos todos en una sola línea.

tybro0103
fuente
¿Qué quieres decir con "no guardar el registro"?
Anatoly
update_attributes guarda el modelo de la base de datos. Me pregunto si hay un método similar que no lo hace.
tybro0103
3
atribuye el método no destructivo. Ver API para más detalles
Anatoly
3
Puede usar update_column (nombre, valor) Actualiza un solo atributo de un objeto, sin llamar a guardar. 1. Se omite la validación. 2. Las devoluciones de llamada se omiten. 3. updated_at / updated_on columna no se actualiza si esa columna está disponible. apidock.com/rails/ActiveRecord/Persistence/update_column
Antoine
10
Para 3.1+, use assign_attributes apidock.com/rails/ActiveRecord/Base/assign_attributes
elado

Respuestas:

597

Creo que lo que estás buscando es assign_attributes.

Básicamente es lo mismo que update_attributes pero no guarda el registro:

class User < ActiveRecord::Base
  attr_accessible :name
  attr_accessible :name, :is_admin, :as => :admin
end

user = User.new
user.assign_attributes({ :name => 'Josh', :is_admin => true }) # Raises an ActiveModel::MassAssignmentSecurity::Error
user.assign_attributes({ :name => 'Bob'})
user.name        # => "Bob"
user.is_admin?   # => false
user.new_record? # => true
Ajedi32
fuente
Su ejemplo es un poco engañoso ya que no ha pegado esta línea del modelo attr_accessible :is_admin, :as => :admin:;)
Robin
@Robin O simplemente: attr_protected :is_admin. O: attr_accessible :nameEl punto es que en este ejemplo,: is_admin está protegido. También debo tener en cuenta que el intento de asignar en masa un atributo protegido .assign_attributessí genera un ActiveModel::MassAssignmentSecurity::Error, aunque eso no se muestra en el ejemplo.
Ajedi32
Sí, pero mi línea es del documento al que te vinculaste. Solo digo que deberías haber copiado / pegado todo el ejemplo. Pero sí, puedes decir que está protegido.
Robin
@Robin Actualizaré el ejemplo para que sea un poco más específico. El ejemplo en los documentos también es un poco engañoso, ya que no menciona que user.assign_attributes({ :name => 'Josh', :is_admin => true })genera un mensaje de error y en realidad no establece la propiedad del nombre del usuario.
Ajedi32
77
asignar_atributos está disponible desde Rails 3.1 en adelante, por lo que no puede usarlo si todavía está ejecutando una versión anterior de Rails.
Haegin
174

Puedes usar assign_attributeso attributes=(son lo mismo)

Hoja de trucos de métodos de actualización (para Rails 6):

  • update= assign_attributes+save
  • attributes= = alias de assign_attributes
  • update_attributes = obsoleto, alias de update

Fuente:
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/persistence.rb
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/attribute_assignment .rb

Otra hoja de trucos:
http://www.davidverhasselt.com/set-attributes-in-activerecord/#cheat-sheet

Yarin
fuente
1
Claro y corto. Gracias.
freemanoid
1
en el caso de .attributes = val, si su modelo tiene uno y acepta atributos asignados por otro modelo, pasar esos atributos de modelo (sin id) eliminará el modelo has_one existente, incluso si no persistió (por ejemplo, guardar). Pero asignar_atributos no se comporta así.
ClassyPimp
65

Puede usar el método 'atributos':

@car.attributes = {:model => 'Sierra', :years => '1990', :looks => 'Sexy'}

Fuente: http://api.rubyonrails.org/classes/ActiveRecord/Base.html

atributos = (nuevos_atributos, guard_protected_attributes = verdadero) Le permite configurar todos los atributos a la vez al pasar un hash con claves que coinciden con los nombres de los atributos (que nuevamente coincide con los nombres de las columnas).

Si guard_protected_attributes es verdadero (el valor predeterminado), los atributos confidenciales pueden protegerse de esta forma de asignación masiva mediante el uso de la macro attr_protected. O también puede especificar a qué atributos se puede acceder con la macro attr_accessible. Entonces, no se permitirá que todos los atributos no incluidos se asignen en masa.

class User < ActiveRecord::Base
  attr_protected :is_admin
end

user = User.new
user.attributes = { :username => 'Phusion', :is_admin => true }
user.username   # => "Phusion"
user.is_admin?  # => false

user.send(:attributes=, { :username => 'Phusion', :is_admin => true }, false)
user.is_admin?  # => true
bruno077
fuente
7

Para la asignación masiva de valores a un modelo ActiveRecord sin guardar, use el assign_attributesoattributes= métodos . Estos métodos están disponibles en Rails 3 y posteriores. Sin embargo, hay pequeñas diferencias y problemas relacionados con la versión a tener en cuenta.

Ambos métodos siguen este uso:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }

@user.attributes = { model: "Sierra", year: "2012", looks: "Sexy" }

Tenga en cuenta que ninguno de los métodos realizará validaciones o ejecutará devoluciones de llamada; las devoluciones de llamada y la validación sucederán cuandosave se llame.

Carriles 3

attributes=difiere ligeramente de assign_attributesen Rails 3. attributes=verificará que el argumento que se le haya pasado sea un Hash y regrese inmediatamente si no lo es; assign_attributesno tiene tal verificación de Hash. Consulte la documentación de la API de asignación de atributos de ActiveRecord paraattributes= .

El siguiente código no válido fallará silenciosamente simplemente regresando sin establecer los atributos:

@user.attributes = [ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ]

attributes= silenciosamente se comportará como si las asignaciones se hicieran con éxito, cuando en realidad no lo fueron.

Este código no válido generará una excepción cuando assign_attributesintente stringificar las claves hash de la matriz adjunta:

@user.assign_attributes([ { model: "Sierra" }, { year: "2012" }, { looks: "Sexy" } ])

assign_attributesgenerará una NoMethodErrorexcepción para stringify_keys, lo que indica que el primer argumento no es un Hash. La excepción en sí misma no es muy informativa sobre la causa real, pero el hecho de que ocurra una excepción es muy importante.

La única diferencia entre estos casos es el método utilizado para la asignación en masa: attributes=silenciosamente tiene éxito y assign_attributesgenera una excepción para informar que se ha producido un error.

Estos ejemplos pueden parecer artificiales, y lo son hasta cierto punto, pero este tipo de error puede ocurrir fácilmente al convertir datos de una API, o incluso simplemente usando una serie de transformación de datos y olvidando Hash[]los resultados finales .map. Mantenga algunas líneas de código 50 arriba y 3 funciones eliminadas de su asignación de atributos, y tiene una receta para fallar.

La lección con Rails 3 es esta: siempre use en assign_attributeslugar de attributes=.

Carriles 4

En Rails 4, attributes=es simplemente un alias para assign_attributes. Consulte la documentación de la API de asignación de atributos de ActiveRecord paraattributes= .

Con Rails 4, cualquiera de los métodos se puede usar indistintamente. No pasar un hash como primer argumento dará como resultado una excepción muy útil:ArgumentError: When assigning attributes, you must pass a hash as an argument.

Validaciones

Si está realizando un vuelo previo a las tareas en preparación para una save, también podría estar interesado en validar antes de guardar. Puede usar los métodos valid?y invalid?para esto. Ambos devuelven valores booleanos. valid?devuelve verdadero si el modelo no guardado pasa todas las validaciones o falso si no lo hace. invalid?es simplemente el inverso devalid?

valid? se puede usar así:

@user.assign_attributes{ model: "Sierra", year: "2012", looks: "Sexy" }.valid?

Esto le dará la capacidad de manejar cualquier problema de validación antes de llamar save.

Michael Gaskill
fuente