Używam after_commit w mojej aplikacji.after_commit dla atrybutu
Chciałbym, aby zadziałało tylko wtedy, gdy dane pole zostanie zaktualizowane w moim modelu. Czy ktoś wie, jak to zrobić?
Używam after_commit w mojej aplikacji.after_commit dla atrybutu
Chciałbym, aby zadziałało tylko wtedy, gdy dane pole zostanie zaktualizowane w moim modelu. Czy ktoś wie, jak to zrobić?
Wygląda na to, że chcesz uzyskać coś takiego jak conditional callback. Gdybyś pisał jakiś kod mógłbym wskazał w dobrym kierunku, jednak myślę, że chcesz używać coś takiego:
after_commit :callback,
:if => Proc.new { |record| record.field_modified? }
I nie sądzę, że można to zrobić w after_commit
The after_commit nazywa po transakcji jest zobowiązana Rails Transactions
na przykład w moim konsoli szyn
> 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
dlatego nie będzie mógł użyć warunku :if
na after_commit
, ponieważ atrybut nie będzie już oznaczony jako zmieniony, ponieważ został zapisany. Może być konieczne sprawdzenie, czy pole, na które się znajdujesz, to changed?
w innym wywołaniu zwrotnym przed zapisaniem rekordu?
Odpowiedź na to stare pytanie, ponieważ nadal pojawia się w wynikach wyszukiwania
można użyć metody previous_changes który returnes hash w formacie:
{ „changed_attribute” => [ "stare wartości ”, "nowa wartość"]}
to co changes dopiero rekord zostanie faktycznie zapisane (od active_record/attribute_methods/dirty.rb):
def save(*) #:nodoc:
if status = super
@previously_changed = changes
@changed_attributes.clear
# .... whatever goes here
więc w Twoim przypadku można sprawdzić previous_changes.key? "your_attribute"
czy coś takiego
stare pytanie, ale ta jest jedną z metod, które znalazłem, że może pracować z after_commit zwrotnego (działa od paukul's answer). Przynajmniej obie wartości utrzymują się po zatwierdzeniu w IRB.
after_commit :callback,
if: proc { |record|
record.previous_changes.key?(:attribute) &&
record.previous_changes[:attribute].first != record.previous_changes[:attribute].last
}
Na podstawie [dokumentacja] (http://apidock.com/rails/ActiveModel/Dirty/previous_changes), myślę, że 'keys' musi być wywołana:' record.previous_changes.keys.include? (: Attribute) '. Dzięki tej zmianie działa to dla mnie. – milkfarm
'record.previous_changes.key? (: Attribute)' jest wystarczającym warunkiem, ponieważ pierwsza i ostatnia wartość nie zawsze są równe. –
@jamesdevar ma rację, ale to rozwiązanie może się nie udać, jeśli model zostanie zapisany więcej niż jeden raz podczas transakcji. Oto przykładowy projekt Railsów https://github.com/ccmcbeck/after-commit w celu zademonstrowania problemu i mojego rozwiązania, które go naprawi. –
Użyj klejnot ArTransactionChanges. previous_changes
nie działa na mnie w Rails 4.0.x
Zastosowanie:
class User < ActiveRecord::Base
include ArTransactionChanges
after_commit :print_transaction_changes
def print_transaction_changes
transaction_changed_attributes.each do |name, old_value|
puts "attribute #{name}: #{old_value.inspect} -> #{send(name).inspect}"
end
end
end
Jest to bardzo stary problem, ale akceptowane previous_changes
rozwiązanie po prostu nie jest wystarczająco wytrzymałe. W transakcji ActiveRecord
istnieje wiele powodów, dla których możesz dwukrotnie zapisać model. previous_changes
odzwierciedla jedynie wynik końcowej wersji save
.Rozważmy następujący przykład
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
który Wyjścia:
{"title"=>["1", "2"], "updated_at"=>[...]}
ale, czego potrzebujesz to:
{"title"=>["1", "2"], "number"=>[1, 2], "updated_at"=>[...]}
Więc moje rozwiązanie to:
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
którego można wypróbować tutaj: https://github.com/ccmcbeck/after-commit
jest zmieniona w polu? gotowa metoda czy jest to metoda, którą muszę napisać, aby sprawdzić wartość pola? zasadniczo próbuję ponownie obliczyć i zapisać ocenę użytkownika na podstawie wszystkich jego ocen spotkań. Ocena do spotkania może zostać dodana po utworzeniu, więc chciałbym się dowiedzieć, czy pole oceny zostało zaktualizowane. Oto niektóre z kodu, które pracuję z klasy Powołanie
alik
Nope sprawdzić wartość pola. –