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


у меня есть модель пользователя, которая имеет атрибут :credits. Я хочу простую кнопку, которая будет добавлять 5 в кредит пользователя, по пути, под названием "добавить" так, что /пользователи/3/Добавить добавить 5 к кредитов идентификатор пользователя = 3.

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

это соответствующая часть моего контроллера. Проблема в том, что я не хочу звонить @user.сохранить, потому что у меня есть обратный вызов 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

EDIT 2:

хорошо, это была проблема проверки, которая сделала изменения в "EDIT" не работают, но я все равно хотел бы получить ответ на исходный вопрос об обновлении без обратных вызовов!

9 60

9 ответов:

рельсы 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

Я думаю, что вы должны использовать метод update_counters в этом случае. Используйте его так в действии контроллера:

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

как общий ответ, в Rails 4 это простой способ обновить атрибуты без запуска обратных вызовов:

@user.update_column 'credits', 5

Если вам нужно обновить несколько атрибутов, не вызывает обратные вызовы:

@user.update_columns credits: 5, bankrupt: false  

есть и другие варианты здесь в направляющих рельсов если вы предпочитаете, но я нашел этот способ самый простой.

вы должны быть в состоянии использовать 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?.