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?

pcasa
fonte

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
Peter Brown
fonte
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

campeterson
fonte
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.

hamstar
fonte
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

Lucas Andrade
fonte
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

shubham mishra
fonte