Los rieles 3 comprueban si el atributo cambió

153

Es necesario verificar si un bloque de atributos ha cambiado antes de actualizar en Rails 3.

calle1, calle2, ciudad, estado, código postal

Sé que podría usar algo como

if @user.street1 != params[:user][:street1]
  then do something....
end

Pero ese código será REALMENTE largo. ¿Hay una manera más limpia?

pcasa
fuente

Respuestas:

282

Consulte ActiveModel :: Dirty (disponible en todos los modelos de forma predeterminada). La documentación es realmente buena, pero le permite hacer cosas como:

@user.street1_changed? # => true/false
Peter Brown
fuente
Esto no ayuda si está tratando de ver si un atributo se modificó en una forma (que parece ser lo que está haciendo el OP).
Mark Fraser
8
Claro que si. Si asigna los valores utilizando el hash de params, estos provienen del formulario.
Peter Brown el
¿Qué pasaría si quisiera hacer algo como esto: model.collection.changed_size_by? o algo así
Mauro Dias
@MauroDias tendrías que escribir tu propia lógica para rastrear propiedades de colección como esa.
Peter Brown
Intentaré usar alguna lib de prueba para esto. puede ser una solución, de todos modos TY
Mauro Dias
54

Así es como resolví el problema de verificar los cambios en múltiples atributos.

attrs = ["street1", "street2", "city", "state", "zipcode"]

if (@user.changed & attrs).any?
  then do something....
end

El changedmétodo devuelve una matriz de los atributos modificados para ese objeto.

Ambos @user.changedy attrsson matrices para que pueda obtener la intersección (ver ary & other arymétodo). El resultado de la intersección es una matriz. Al llamar any?a la matriz, me vuelvo verdadero si hay al menos una intersección.

También es muy útil, el changed_attributesmétodo devuelve un hash de los atributos con sus valores originales y changesdevuelve un hash de los atributos con sus valores originales y nuevos (en una matriz).

Puede verificar APIDock para saber qué versiones admiten estos métodos.

http://apidock.com/rails/ActiveModel/Dirty

campeterson
fuente
1
Esta es una buena manera de hacerlo. Históricamente lo he hecho. if attrs.any?{|attr| @user.send("#{attr}_changed?")}Cuando quiero verificar si uno de los diferentes atributos ha cambiado. Por supuesto, solo hago esto con atributos que yo mismo he definido, porque no me gusta incluir parámetros de usuario en un sendmétodo. ;)
nzifnab
14

ActiveModel::Dirtyno funcionó para mí porque @model.update_attributes()escondió los cambios. Así es como detecté cambios en un updatemétodo en un controlador:

def update
  @model = Model.find(params[:id])
  detect_changes

  if @model.update_attributes(params[:model])
    do_stuff if attr_changed?
  end
end

private

def detect_changes
  @changed = []
  @changed << :attr if @model.attr != params[:model][:attr]
end

def attr_changed?
  @changed.include :attr
end

Sin embargo, si intenta detectar muchos cambios en los atributos, podría ser complicado. Probablemente no debería hacer esto en un controlador, pero meh.

hamstar
fuente
9
Para lo que vale, puede hacer esto con el previous_changesque también está disponible de forma predeterminada.
Jason Galuten
2
#update_attributesNo oculta los cambios. Guarda el registro, por lo que el modelo se actualiza y no hay cambios. Debe cambiar el campo en el modelo y verificar changed?antes de guardarlo. es decir. @ model.field = 'foo' o@model.attributes = @model.attributes.merge(params[:model])
Grzegorz
9

Para rieles 5.1+ devoluciones de llamada

A partir de Ruby on Rails 5.1, los métodos attribute_changed?y attribute_wasActiveRecord quedarán en desuso

Usar en saved_change_to_attribute?lugar deattribute_changed?

@user.saved_change_to_street1? # => true/false

Más ejemplos aquí.

Lucas Andrade
fuente
2

Las respuestas anteriores son mejores, pero para el conocimiento también tenemos otra aproximación, cambiemos el valor de la columna 'catagory' para un objeto (@design),

@design.changes.has_key?('catagory')

Los .changes devolverán un hash con la clave como nombre de columna y los valores como una matriz con dos valores [old_value, new_value] para cada columna. Por ejemplo, la categoría de arriba se cambia de 'ABC' a 'XYZ' de @design,

@design.changes   # => {} 
@design.catagory = 'XYZ'
@design.changes # => { 'catagory' => ['ABC', 'XYZ'] }

Para referencias cambiar en ROR

Shubham Mishra
fuente