Рейки 3 перевіряють, чи змінився атрибут


153

Потрібно перевірити, чи змінився блок атрибутів перед оновленням в Rails 3.

street1, street2, місто, штат, поштовий індекс

Я знаю, що міг би використовувати щось подібне

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

Але цей фрагмент коду буде дійсно довгим. Чи є чистіший спосіб?

Відповіді:


282

Ознайомтеся з ActiveModel :: брудно (доступно для всіх моделей за замовчуванням). Документація справді хороша, але дозволяє робити такі дії, як:

@user.street1_changed? # => true/false

Це не допомагає, якщо ви намагаєтесь дізнатись, чи змінився атрибут у формі (що, здається, те, що робить ОП).
Марк Фрейзер

8
Звичайно, це і є. Якщо ви присвоюєте значення за допомогою хеш-парамів, вони походять з форми.
Пітер Браун

що робити, якщо я хотів би зробити щось подібне: model.collection.changed_size_by? чи щось на кшталт
Mauro Dias

@MauroDias вам доведеться написати власну логіку для відстеження таких властивостей колекції.
Пітер Браун

Я спробую використати для цього тест-ліб. може бути вирішеним, все одно TY
Mauro Dias

54

Ось так я вирішив проблему перевірки змін у кількох атрибутах.

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

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

changedМетод повертає масив атрибутів змінених для цього об'єкта.

Обидва @user.changedі attrsє масивами, щоб я міг отримати перехрестя (див. ary & other aryМетод). Результатом перетину є масив. Зателефонувавши any?до масиву, я отримую істину, якщо є хоча б одне перехрестя.

Також дуже корисно, changed_attributesметод повертає хеш атрибутів з їх початковими значеннями та changesповертає хеш атрибутів з їх початковими та новими значеннями (у масиві).

Ви можете перевірити APIDock, для яких версій підтримуються ці методи.

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


1
Це приємний спосіб зробити це. Історично я це робив if attrs.any?{|attr| @user.send("#{attr}_changed?")}Коли я хочу перевірити, чи змінився один з декількох різних атрибутів - Звичайно, я це роблю лише з атрибутами, які я сам визначив, тому що мені не подобається вводити параметри користувачів у sendметод. ;)
nzifnab

14

ActiveModel::Dirtyне працював для мене, тому що @model.update_attributes()приховав зміни. Ось так я виявив зміни в updateметоді в контролері:

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

Якщо ви намагаєтесь виявити багато змін атрибутів, він може стати безладним. Можливо, це не слід робити в контролері, але мех.


9
Для чого це варто, ви можете зробити це з тим, previous_changesщо також доступно за замовчуванням.
Джейсон Галутен

2
#update_attributesне приховує змін. Це зберігає запис, тому модель оновлюється і змін немає. Потрібно змінити поле в моделі та перевірити наявність, changed?перш ніж зберегти його. тобто. @ model.field = 'foo' або@model.attributes = @model.attributes.merge(params[:model])
Гжегож

9

Для рейкових 5.1+ зворотних дзвінків

Станом на Ruby on Rails 5.1 методи attribute_changed?та attribute_wasActiveRecord будуть застарілими

Використовуйте saved_change_to_attribute?замістьattribute_changed?

@user.saved_change_to_street1? # => true/false

Більше прикладів тут


2

Наведені вище відповіді є кращими, але для знань у нас є ще одна відповідність. Давайте змінено значення стовпця "Каталог" для об'єкта (@design),

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

Зміни повертають хеш із ключем як ім'ям стовпця та значеннями як масив із двома значеннями [old_value, new_value] для кожного стовпця. Наприклад, катагорія для вищезгаданих змінюється з "ABC" на "XYZ" від @design,

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

Для зміни посилань у ROR

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.