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.