Rails 3 vérifie si l'attribut a changé

153

Besoin de vérifier si un bloc d'attributs a changé avant la mise à jour dans Rails 3.

street1, street2, ville, état, code postal

Je sais que je pourrais utiliser quelque chose comme

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

Mais ce morceau de code sera VRAIMENT long. Y a-t-il un moyen plus propre?

pcasa
la source

Réponses:

282

Découvrez ActiveModel :: Dirty (disponible sur tous les modèles par défaut). La documentation est vraiment bonne, mais elle vous permet de faire des choses telles que:

@user.street1_changed? # => true/false
Peter Brown
la source
Cela n'aide pas si vous essayez de voir si un attribut a été modifié dans un formulaire (ce qui semble être ce que fait l'OP).
Mark Fraser
8
Bien sûr que oui. Si vous affectez les valeurs à l'aide du hachage de paramètres, celles-ci proviennent du formulaire.
Peter Brown
et si je voulais faire quelque chose comme ça: model.collection.changed_size_by? ou quelque chose comme
Mauro Dias
@MauroDias vous devrez écrire votre propre logique pour suivre les propriétés de la collection comme ça.
Peter Brown
Je vais essayer d'utiliser une librairie de test pour ça. peut être une solution de contournement, de toute façon TY
Mauro Dias
54

C'est ainsi que j'ai résolu le problème de la vérification des changements dans plusieurs attributs.

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

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

La changedméthode renvoie un tableau des attributs modifiés pour cet objet.

Les deux @user.changedet attrssont des tableaux pour que je puisse obtenir l'intersection (voir ary & other aryméthode). Le résultat de l'intersection est un tableau. En appelant any?le tableau, je deviens vrai s'il y a au moins une intersection.

Également très utile, la changed_attributesméthode renvoie un hachage des attributs avec leurs valeurs d'origine et le changesretourne un hachage des attributs avec leurs valeurs d'origine et nouvelles (dans un tableau).

Vous pouvez vérifier APIDock pour quelles versions ont pris en charge ces méthodes.

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

Campeterson
la source
1
C'est une bonne façon de procéder. Historiquement, je l'ai fait if attrs.any?{|attr| @user.send("#{attr}_changed?")}Quand je veux vérifier si l'un des différents attributs a changé - Bien sûr, je ne le fais qu'avec des attrs que j'ai moi-même définis, car je n'aime pas jeter les paramètres utilisateur dans une sendméthode. ;)
nzifnab
14

ActiveModel::Dirtyn'a pas fonctionné pour moi parce que le @model.update_attributes()cache les changements. Voici donc comment j'ai détecté des changements dans une updateméthode dans un contrôleur:

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

Si vous essayez de détecter de nombreux changements d'attributs, cela peut devenir compliqué. Ne devrait probablement pas faire cela dans un contrôleur, mais meh.

hamstar
la source
9
Pour ce que ça vaut, vous pouvez le faire avec ce previous_changesqui est également disponible par défaut.
Jason Galuten
2
#update_attributesne cache pas les changements. Il enregistre l'enregistrement, le modèle est donc mis à jour et il n'y a pas de changement. Vous devez modifier le champ dans le modèle et vérifier changed?avant de l'enregistrer. c'est à dire. @ model.field = 'foo' or@model.attributes = @model.attributes.merge(params[:model])
Grzegorz
9

Pour les rappels de rails 5.1+

À partir de Ruby on Rails 5.1, les méthodes attribute_changed?et attribute_wasActiveRecord seront obsolètes

Utiliser à la saved_change_to_attribute?place deattribute_changed?

@user.saved_change_to_street1? # => true/false

Plus d'exemples ici

Lucas Andrade
la source
2

Les réponses ci-dessus sont meilleures mais pour la connaissance, nous avons également une autre approche, la valeur de la colonne 'catagory' change pour un objet (@design),

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

Les .changes renverront un hachage avec une clé comme nom de colonne et des valeurs sous forme de tableau avec deux valeurs [old_value, new_value] pour chaque colonne. Par exemple, la catégorie ci-dessus passe de «ABC» à «XYZ» de @design,

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

Pour le changement de références dans ROR

Shubham Mishra
la source