J'utilise un after_commit dans mon application.
Je voudrais qu'il se déclenche uniquement lorsqu'un champ particulier est mis à jour dans mon modèle. Quelqu'un sait-il comment faire ?
J'utilise un after_commit dans mon application.
Je voudrais qu'il se déclenche uniquement lorsqu'un champ particulier est mis à jour dans mon modèle. Quelqu'un sait-il comment faire ?
Vieille question, mais voici une méthode que j'ai trouvée et qui pourrait fonctionner avec le callback after_commit (à partir de Réponse de paukul ). Au moins, les valeurs persistent toutes deux après la validation dans l'IRB.
after_commit :callback,
if: proc { |record|
record.previous_changes.key?(:attribute) &&
record.previous_changes[:attribute].first != record.previous_changes[:attribute].last
}
Sur la base de la documentation Je pense que keys
doit être appelé : record.previous_changes.keys.include?(:attribute)
. Avec cette modification, cela fonctionne pour moi.
record.previous_changes.key?(:attribute)
est une condition suffisante, car la première et la dernière valeur ne sont jamais égales.
@jamesdevar a raison, mais cette solution peut échouer si votre modèle est sauvegardé plus d'une fois pendant la transaction. Voici un exemple de projet Rails github.com/ccmcbeck/after-commit pour démontrer le problème et la solution que j'ai trouvée pour le résoudre.
Répondre à cette vieille question parce qu'elle apparaît encore dans les résultats de recherche
vous pouvez utiliser le changements_précédents qui renvoie un hachage du format :
{ "changed_attribute" => ["old value", "new value"] }
c'est ce que changements jusqu'à ce que l'enregistrement soit réellement sauvegardé (à partir de active_record/attribute_methods/dirty.rb) :
def save(*) #:nodoc:
if status = super
@previously_changed = changes
@changed_attributes.clear
# .... whatever goes here
Dans votre cas, vous pouvez donc vérifier previous_changes.key? "your_attribute"
ou quelque chose comme ça
Je ne pense pas que vous puissiez le faire en after_commit
La fonction after_commit est appelée après que la transaction a été validée. Transactions Rails
Par exemple, dans ma console rails
> record = MyModel.find(1)
=> #<MyModel id: 1, label: "test", created_at: "2011-08-19 22:57:54", updated_at: "2011-08-19 22:57:54">
> record.label = "Changing text"
=> "Changing text"
> record.label_changed?
=> true
> record.save
=> true
> record.label_changed?
=> false
Par conséquent, vous ne pourrez pas utiliser la fonction :if
condition sur after_commit
car l'attribut ne sera plus marqué comme modifié puisqu'il a été enregistré. Il se peut que vous deviez déterminer si le champ que vous recherchez est changed?
dans un autre callback avant que l'enregistrement ne soit sauvegardé ?
Il s'agit d'un problème très ancien, mais l'on s'accorde à dire qu'il n'a pas été résolu. previous_changes
n'est tout simplement pas assez robuste. Dans un ActiveRecord
Il y a de nombreuses raisons pour lesquelles vous pouvez sauver un modèle deux fois. previous_changes
ne reflète que le résultat de la dernière save
. Prenons l'exemple suivant
class Test < ActiveRecord::Base
after_commit: :after_commit_test
def :after_commit_test
puts previous_changes.inspect
end
end
test = Test.create(number: 1, title: "1")
test = Test.find(test.id) # to initialize a fresh object
test.transaction do
test.update(number: 2)
test.update(title: "2")
end
qui produit :
{"title"=>["1", "2"], "updated_at"=>[...]}
mais ce qu'il faut, c'est.. :
{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}
Ma solution est donc la suivante :
module TrackSavedChanges
extend ActiveSupport::Concern
included do
# expose the details if consumer wants to do more
attr_reader :saved_changes_history, :saved_changes_unfiltered
after_initialize :reset_saved_changes
after_save :track_saved_changes
end
# on initalize, but useful for fine grain control
def reset_saved_changes
@saved_changes_unfiltered = {}
@saved_changes_history = []
end
# filter out any changes that result in the original value
def saved_changes
@saved_changes_unfiltered.reject { |k,v| v[0] == v[1] }
end
private
# on save
def track_saved_changes
# maintain an array of ActiveModel::Dirty.changes
@saved_changes_history << changes.dup
# accumulate the most recent changes
@saved_changes_history.last.each_pair { |k, v| track_saved_change k, v }
end
# v is an an array of [prev, current]
def track_saved_change(k, v)
if @saved_changes_unfiltered.key? k
@saved_changes_unfiltered[k][1] = track_saved_value v[1]
else
@saved_changes_unfiltered[k] = v.dup
end
end
# type safe dup inspred by http://stackoverflow.com/a/20955038
def track_saved_value(v)
begin
v.dup
rescue TypeError
v
end
end
end
que vous pouvez essayer ici : https://github.com/ccmcbeck/after-commit
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.