Rails: оновіть атрибут моделі без виклику зворотних викликів


76

У мене є модель користувача, яка має атрибут: credits. Я хочу просту кнопку, яка додасть 5 до кредитів користувача через маршрут, що називається "додати", щоб / users / 3 / add додав 5 до кредитів ідентифікатора користувача = 3.

def add
    @user = User.find(params[:id])
    @user.credits += 5
    redirect_to root_path
end

Це відповідна частина мого контролера. Проблема в тому, що я не хочу телефонувати @ user.save, тому що у мене є зворотний виклик before_save, який повторно шифрує пароль користувача на основі поточного часу UTC. Я просто хочу просто додати 5 до атрибуту та уникнути зворотного виклику, я ніколи не думав, що така проста річ може бути такою важкою.

РЕДАГУВАТИ:

Я змінив зворотний виклик на: before_create, ось мій новий код контролера (відповідна частина):

  def add
    @user = User.find(params[:id])
    @user.add_credits(5)
    @user.save
    flash[:success] = "Credits added!"
    redirect_to root_path
  end

і ось мій код у моделі:

 def add_credits(num)
    self.credits = num
 end

РЕДАКТУВАТИ 2:

Гаразд, це була проблема перевірки, через яку зміни в "EDIT" не працювали, але я все одно хотів би отримати відповідь на вихідне питання оновлення без зворотних викликів!


Я надав посилання зі списком методів, які не викликають зворотних викликів, і ми з Finbarr запропонували використовувати умовний зворотний виклик - які додаткові рішення ви шукаєте?
Dave Newton

Відповіді:


142

Введено Rails 3.1 update_column, що є тим самим, що і є update_attribute, але без спроби перевірки або зворотних викликів:

http://apidock.com/rails/ActiveRecord/Persistence/update_column


Я хотів би цим скористатися, але я не можу перейти на Rails 3.1 чисто, або, принаймні, я не знаю як.
Сем Стерн,

1
Яку версію Rails ви використовуєте на даний момент?
cvshepherd

15

Як загальну відповідь, у Rails 4 це простий спосіб оновити атрибути, не викликаючи зворотних викликів:

@user.update_column :credits, 5

Якщо вам потрібно оновити кілька атрибутів, не викликаючи зворотних викликів:

@user.update_columns credits: 5, bankrupt: false  

Є інші варіанти тут, у "Посібниках рейок", якщо вам більше подобається, але я знайшов цей спосіб найпростішим.


4
кредити user.update_column: 5 не працює. Це має бути user.update_column 'кредити', 5
Richard H Fung

Я використовую Rails 4.2, і це, безумовно, працює для мене. Цей рядок генерує такий запит:UPDATE "users" SET "credits" = $1, "updated_at" = $2 WHERE "users"."id" = $3 [["credits", 5], ["updated_at", "2017-04-01 21:34:52.746626"], ["id", 1]]
Метт

1
Під час використання update_columnви не можете передати йому такий об’єкт, як credits: 5. Потрібно передати два параметри, причому перший - це ім’я стовпця, а другий - значення, яке потрібно оновити. Ви можете це зробити update_column :credits, 5, що, як правило, було б кращим, ніж використання рядка для імені стовпця.
Джошуа Пінтер

8

Щоб оновити кілька атрибутів без зворотних викликів, ви можете використовувати update_all у своїй моделі так:

self.class.update_all({name: value, name: value}, self.class.primary_key => id)

Якщо ви дійсно хочете, ви можете спробувати навіть метод update_columns і змішати це з вашим активним базовим класом запису.

Щоб оновити один атрибут, ви можете використовувати update_column. Окрім того, є деякі конкретні методи, які можна знайти в керівництві по рейках http://guides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks


6

Для mongoid я в кінцевому підсумку використовував http://mongoid.org/en/mongoid/docs/persistence.html Зокрема, ви можете використовувати:

person.set(name:"Robert Pulson")

і зворотного виклику не буде. Так кльово.


its person.set (: name, "Robert Pulson")
Джуд Калімбас

2
@JudeCalimbas немає , це не так , ArgumentError: невірне число аргументів (дані 2, очікуваного 1)
ПОВТОР

3

Я думаю, вам слід використовувати метод update_counters у цьому випадку. Використовуйте його таким чином у своїй дії контролера:

def add
  User.update_counters params[:id], :credits => 5
  redirect_to root_path
end

2

Ви повинні мати можливість використовувати update_all, щоб уникнути зворотних викликів.

def add
 @user = User.find(params[:id])
 User.where(:id=>@user.id).update_all(:credits => @user.credits+5)
 redirect_to root_path
end

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



1

Можливо, ваш інший хук before_save повинен перевірити, чи насправді змінився пароль користувача, перш ніж шифрувати його знову.


1

У вас є кілька варіантів, в тому числі зміни , які CallBack ви використовуєте, наприклад, after_create.

Ви можете оновити стовпці, не викликаючи зворотних викликів, див. Пропуск зворотних викликів у посібнику AR. Наприклад, update_columnне викликає зворотних викликів. Попереднє посилання перелічує функції, що не запускають.

Ви також можете використовувати будь-яку форму умовного зворотного дзвінка (або навіть спостерігача) для зміни пароля. Див ActiveModel :: Брудні , наприклад, @user.password_changed?.


Гаразд, я змінив його на "after_create", але він все одно не буде працювати! Перегляньте моє редагування для мого нового коду, я не можу отримати кредити для зміни.
Sam Stern

@hatboysam Ну, це робить щось інше - раніше ви додавали, тепер ви налаштовуєте. Використовуйте, save!щоб перевірити, чи є виняток (та / або перевірити журнали та / або видалити глушники зворотного відстеження).
Dave Newton

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