Usando ActiveRecord, há uma maneira de obter os valores antigos de um registro durante after_update

StackOverflow https://stackoverflow.com/questions/607069

Pergunta

Configuração usando um exemplo simples: Eu tenho uma tabela (Totals) que detém a soma da coluna amount de cada registro em uma segunda tabela (Things)

.

Quando um thing.amount é atualizado, eu gostaria de simplesmente adicionar a diferença entre o valor antigo eo novo valor para total.sum.

Agora eu estou subtraindo self.amount durante before_update e adicionando self.amount durante after_update. Este lugares WAY demasiada confiança na atualização seguinte.

Restrição: Não quero simplesmente recalcular a soma de todas as transações

.

Pergunta: Muito simplesmente, eu gostaria de acessar o valor original durante uma chamada de retorno after_update. Que forma você chegar a fazer isso?

Update: eu estou indo com a idéia de Luke Francl. Durante uma chamada de retorno after_update você ainda tem acesso aos valores self.attr_was que é exatamente o que eu queria. Eu também decidiu ir com uma implementação after_update porque eu quero manter este tipo de lógica no modelo. Dessa forma, não importa o quanto eu decidir transações de atualização no futuro, eu vou saber que estou atualizando a soma das transações corretamente. Obrigado a todos por suas sugestões de implementação.

Foi útil?

Solução

Ditto o que todo mundo está dizendo sobre transações.

Dito isto ...

ActiveRecord a partir de Rails 2.1 mantém o controle dos valores de atributo de um objeto. Então, se você tem um total atributo, você terá um método total_changed? e um método total_was que retorna o valor antigo.

Não há necessidade de acrescentar nada ao seu modelo de acompanhar isso mais.

Update: Aqui está a documentação para ActiveModel :: sujo conforme solicitado.

Outras dicas

Algumas outras pessoas estão mencionando envolvendo tudo isso em uma transação, mas acho que isso é feito para você; você só precisa acionar a reversão levantando uma exceção para erros na after_ * retornos de chamada.

http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

Toda a cadeia de chamada de retorno de um salve, salve !, ou destruir chamada executado dentro de uma transação. Isso inclui after_ ganchos *. Se tudo correr bem um COMMIT é executada uma vez que a cadeia tenha sido concluída.

Se um before_ * callback cancela a ação de um ROLLBACK é emitido. Você também pode desencadear uma ROLLBACK levantar uma exceção em qualquer um dos retornos de chamada, incluindo after_ ganchos *. Note, no entanto, que, nesse caso, o cliente precisa estar ciente disso, porque um simples salvar elevará tal exceção em vez de silêncio retornar false.

acrescentando "_was" para o seu atributo vai dar-lhe o valor anterior antes de salvar os dados.

Esses métodos são chamados métodos sujos .

Felicidades!

Para obter todos os campos alterados, com seus valores antigos e novos, respectivamente:

person = Person.create!(:name => 'Bill')
person.name = 'Bob'
person.save
person.changes        # => {"name" => ["Bill", "Bob"]}

ActiveRecord :: sujo é um módulo que está embutido no ActiveRecord para rastreamento alterações de atributo. Assim você pode usar thing.amount_was para obter o valor antigo.

Adicione esta ao seu modelo:

def amount=(new_value)
    @old_amount = read_attribute(:amount)
    write_attribute(:amount,new_value)
end

Em seguida, use @old_amount em seu código after_update.

Em primeiro lugar, você deve fazer isso em uma transação para garantir que seus dados é escrito juntos.

Para responder à sua pergunta, você poderia apenas definir uma variável membro para o valor antigo no before_update, que você pode então acesso no after_update, no entanto, isso não é uma solução muito elegante.

Idea 1: Enrole a atualização em uma transação de banco de dados, de modo que se a atualização falhar os totais da tabela não é alterado: ActiveRecord Transações docs

Idea. 2: Stash o valor antigo em @old_total durante o before_update

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top