O Rails 3 verifica se o atributo foi alterado


153

É necessário verificar se um bloco de atributos foi alterado antes da atualização no Rails 3.

street1, street2, cidade, estado, código postal

Eu sei que eu poderia usar algo como

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

Mas esse pedaço de código será REALMENTE longo. Existe uma maneira mais limpa?

Respostas:


282

Confira ActiveModel :: Dirty (disponível em todos os modelos por padrão). A documentação é realmente boa, mas permite fazer coisas como:

@user.street1_changed? # => true/false

Isso não ajuda se você estiver tentando ver se um atributo foi alterado em um formulário (que parece ser o que o OP está fazendo).
— Mark Fraser

8
Claro que sim. Se você atribuir os valores usando o hash params, eles vieram do formulário.
— Peter Brown

e se eu quiser fazer algo assim: model.collection.changed_size_by? ou algo assim #
— Mauro Dias

@MauroDias, você teria que escrever sua própria lógica para rastrear as propriedades da coleção assim.
— Peter Brown

Vou tentar usar alguma biblioteca de teste para isso. pode ser uma solução alternativa, de qualquer maneira TY
— Mauro Dias

54

Foi assim que resolvi o problema de verificar alterações em vários atributos.

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

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

O changedmétodo retorna uma matriz dos atributos alterados para esse objeto.

Ambos @user.changede attrssão matrizes para que eu possa obter a interseção (consulte o ary & other arymétodo). O resultado da interseção é uma matriz. Ao chamar any?a matriz, sou verdadeiro se houver pelo menos uma interseção.

Também muito útil, o changed_attributesmétodo retorna um hash dos atributos com seus valores originais e changesretorna um hash dos atributos com seus valores originais e novos (em uma matriz).

Você pode verificar o APIDock para quais versões suportam esses métodos.

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


1
Esta é uma boa maneira de fazê-lo. Historicamente, eu o fiz if attrs.any?{|attr| @user.send("#{attr}_changed?")}Quando quero verificar se um dos vários atributos diferentes foi alterado - Obviamente, só faço isso com os atributos que me defini, porque não gosto de usar parâmetros de usuário em um sendmétodo. ;)
— nzifnab

14

ActiveModel::Dirtynão funcionou para mim porque @model.update_attributes()ocultou as alterações. Então é assim que eu detectei as alterações em um updatemétodo em um controlador:

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

Se você está tentando detectar muitas alterações de atributo, pode ficar confuso. Provavelmente não deveria fazer isso em um controlador, mas meh.


9
Para o que vale a pena, você pode fazer isso com o previous_changesque também está disponível por padrão.
— Jason Galuten

2
#update_attributesnão oculta as alterações. Ele salva o registro, para que o modelo seja atualizado e não haja alterações. Você precisa alterar o campo no modelo e verificar changed?antes de salvá-lo. ie @ model.field = 'foo' ou@model.attributes = @model.attributes.merge(params[:model])
— Grzegorz

9

Para trilhos 5.1 + retornos de chamada

A partir do Ruby on Rails 5.1, os métodos attribute_changed?e attribute_wasActiveRecord serão descontinuados

Use em saved_change_to_attribute?vez deattribute_changed?

@user.saved_change_to_street1? # => true/false

Mais exemplos aqui


2

As respostas acima são melhores, mas, para conhecimento, também temos outra abordagem: o valor da coluna 'categoria' permite que um objeto seja alterado (@design),

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

As alterações. Retornarão um hash com a chave como nome da coluna e os valores como uma matriz com dois valores [old_value, new_value] para cada coluna. Por exemplo, a categoria acima é alterada de 'ABC' para 'XYZ' de @design,

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

Para alteração de referências no ROR

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.