Ответ 1
Rails 3.1 представил update_column
, что совпадает с update_attribute
, но без запуска проверки или обратных вызовов:
http://apidock.com/rails/ActiveRecord/Persistence/update_column
У меня есть модель User, которая имеет атрибут: credits. Я хочу простую кнопку, которая добавит 5 к пользовательским кредитам через маршрут под названием "добавить", чтобы /users/ 3/add добавили 5 к кредитам пользователя id = 3.
def add
@user = User.find(params[:id])
@user.credits += 5
redirect_to root_path
end
Это важная часть моего контроллера. Проблема в том, что я не хочу вызывать @user.save, потому что у меня есть обратный вызов before_save, который заново шифрует пароль пользователя на основе текущего времени UTC. Я просто хочу просто добавить 5 к атрибуту и избежать обратного вызова, я никогда не думал, что такая простая вещь может быть настолько сложной.
EDIT:
Я изменил обратный вызов на: 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" не работали, но я все равно люблю отвечать на исходный вопрос об обновлении без обратных вызовов!
Rails 3.1 представил update_column
, что совпадает с update_attribute
, но без запуска проверки или обратных вызовов:
http://apidock.com/rails/ActiveRecord/Persistence/update_column
Чтобы обновить несколько атрибутов без обратных вызовов, вы можете использовать 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
Как общий ответ, в Rails 4 это простой способ обновить атрибуты без запуска обратных вызовов:
@user.update_column 'credits', 5
Если вам нужно обновить несколько атрибутов без триггеров обратного вызова:
@user.update_columns credits: 5, bankrupt: false
Есть и другие варианты здесь, в руководствах по Rails, если вы предпочитаете, но я нашел этот способ наиболее простым.
Я думаю, вы должны использовать метод update_counters в этом случае. Используйте его так, как показано в действии вашего контроллера:
def add
User.update_counters params[:id], :credits => 5
redirect_to root_path
end
Вы можете использовать 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
Я бы предпочел поместить эту логику в модель, но это должно работать, чтобы решить вашу исходную проблему, как указано в контроллере.
Несколько вариантов, как это сделать в rails4 http://edgeguides.rubyonrails.org/active_record_callbacks.html#skipping-callbacks
Для mongoid я закончил использование http://mongoid.org/en/mongoid/docs/persistence.html В частности, вы можете использовать:
person.set(name:"Robert Pulson")
и не будет выдан обратный вызов. Так здорово.
Возможно, ваш другой крюк before_save должен проверить, действительно ли пароль пользователя был изменен, прежде чем снова зашифровать его.
У вас есть несколько вариантов, в том числе изменение используемого обратного вызова, например after_create
.
Вы можете обновлять столбцы без запуска обратных вызовов, см. Пропуск обратных вызовов в руководстве AR. Например, update_column
не вызывает обратные вызовы. В предыдущей ссылке перечислены функции без запуска.
Вы также можете использовать любую форму Условный обратный вызов (или даже наблюдатель), когда пароль изменен. См. ActiveModel:: Dirty, например @user.password_changed?
.
Вы можете обновить столбец, делая это
User.where (имя: 'lily').update_all (возраст: '10')