web-dev-qa-db-fra.com

Exécuter un rappel uniquement si un attribut a changé dans Rails

J'ai l'association suivante dans mon application:

# Page 
belongs_to :status

Je veux exécuter un rappel à tout moment le status_id d'un page a changé.

Donc si page.status_id va de 4 à 5, je veux pouvoir attraper ça.

Comment le faire

71
Hommer Smith

Rails 5.1+

class Page < ActiveRecord::Base
  before_save :do_something, if: :will_save_change_to_status_id?

  private

  def do_something
    # ...
  end
end

Le commit qui a changé ActiveRecord :: Dirty est ici: https://github.com/Rails/rails/commit/16ae3db5a5c6a08383b974ae6c96faac5b4a3c81

Voici un article de blog sur ces modifications: https://www.ombulabs.com/blog/Rails/upgrades/active-record-5-1-api-changes.html

Voici le résumé que je me suis fait sur les modifications apportées à ActiveRecord :: Dirty dans Rails 5.1+:

ActiveRecord :: Dirty

https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Dirty.html

Avant de sauvegarder (CHANGEMENT FACULTATIF)

Après avoir modifié un objet et avant d’enregistrer dans la base de données, ou dans le filtre before_save:

  • changes devrait maintenant être changes_to_save
  • changed? Devrait maintenant être has_changes_to_save?
  • changed devrait maintenant être changed_attribute_names_to_save
  • <attribute>_change Devrait maintenant être <attribute>_change_to_be_saved
  • <attribute>_changed? Devrait maintenant être will_save_change_to_<attribute>?
  • <attribute>_was Devrait maintenant être <attribute>_in_database

Après la sauvegarde (rupture du changement)

Après avoir modifié un objet et après avoir sauvegardé dans la base de données, ou dans le filtre after_save:

  • saved_changes (Remplace previous_changes)
  • saved_changes?
  • saved_change_to_<attribute>
  • saved_change_to_<attribute>?
  • <attribute>_before_last_save

Rails <= 5.0

class Page < ActiveRecord::Base
  before_save :do_something, if: :status_id_changed?

  private

  def do_something
    # ...
  end
end

Cela utilise le fait que le rappel before_save Peut être exécuté de manière conditionnelle en fonction de la valeur de retour d'un appel de méthode. La méthode status_id_changed? Provient de ActiveModel :: Dirty , ce qui nous permet de vérifier si un attribut spécifique a été modifié en ajoutant simplement _changed? Au nom de l'attribut.

Lorsque la méthode do_something Doit être appelée, vous répondez à vos besoins. Cela peut être before_save Ou after_save Ou n'importe lequel des le ActiveRecord :: Callbacks défini .

145
pdobb

Le attribute_changed? est obsolète dans Rails 5.1, utilisez maintenant simplement will_save_change_to_attribute?.

Pour plus d'informations, voir this issue .

15
Mateus Luiz

Essaye ça

after_validation :do_something, if: ->(obj){ obj.status_id.present? and obj.status_id_changed? } 

def do_something
 # your code
end

Référence - http://apidock.com/Rails/ActiveRecord/Dirty

9
Monideep