Доступ к предыдущему значению ассоциации при обновлении записи - PullRequest
2 голосов
/ 25 февраля 2012

У меня есть модель "события", которая имеет много "приглашений". Приглашения настраиваются с помощью флажков в форме события. Когда событие обновляется, я хотел сравнить приглашения до обновления с приглашениями после обновления. Я хочу сделать это как часть проверки события.

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

Я думал о попытке использовать обратный вызов after_initialize, чтобы сохранить это сам. Эти обратные вызовы, похоже, не соответствуют опции «: on», поэтому я не могу сделать это только: on: update. Я не хочу запускать это каждый раз, когда объект инициализируется.

Есть ли лучший подход к этой проблеме?

Вот код в моем контроллере обновлений:

  def update
  params[:event][:invited_user_ids] ||= []
  if @event.update_attributes(params[:event])
    redirect_to @event
  else
    render action: "edit"
  end
  end

Моя основная цель - сделать так, чтобы вы могли добавлять пользователей к событию, но вы не можете не удалять пользователей. Я хочу подтвердить, что опубликованный параметр visible_user_ids содержит всех приглашенных пользователей.

- обновление
В качестве временного решения я использовал опцию: before_remove в ассоциации: has_many. Я установил его таким образом, чтобы он вызывал исключение ActiveRecord :: RollBack, которое предотвращает нежелание пользователей. Не совсем то, что я хочу, потому что я не могу отобразить ошибку проверки, но она предотвращает ее

Спасибо, Corsen

1 Ответ

3 голосов
/ 25 февраля 2012

Не могли бы вы использовать ActiveModel::Dirty? Примерно так:

def Event < ActiveRecord::Base
  validates :no_invitees_removed

  def no_invitees_removed
    if invitees.changed? && (invitees - invitees_was).present?
      # ... add an error or re-add the missing invitees
    end
  end
end

Редактировать: Я не заметил, что ОП уже обесценили ActiveModel::Dirty, поскольку он не работает с ассоциациями. Мой плохой.

Другая возможность - переопределить метод invited_user_ids=, чтобы добавить существующие идентификаторы пользователя к данному массиву:

class Event < ActiveRecord::Base
  # ...

  def invited_user_ids_with_guard=(ids)
    self.invited_user_ids_without_guard = self.invited_user_ids.concat(ids).uniq
  end
  alias_method_chain :invited_user_ids=, :guard
end

Это все равно должно работать для вас, поскольку update_attributes в конечном счете вызывает отдельные attribute= методы.


Редактировать: @corsen спросил в комментарии, почему я использовал alias_method_chain вместо super в этом примере.

Вызов super работает только тогда, когда вы переопределяете метод, который определен далее в цепочке наследования. Смешивание в модуле или наследование от другого класса предоставляет средства для этого. Этот модуль или класс напрямую не «добавляет» методы к производному классу. Вместо этого он вставляется в цепочку наследования этого класса. Затем вы можете переопределить методы в производном классе, не разрушая первоначальное определение методов (потому что они все еще находятся в суперклассе / модуле).

В этом случае invited_user_ids не определено ни для какого предка Event. Он определяется через метапрограммирование непосредственно в классе Event как часть ActiveRecord. Вызов super в invited_user_ids приведет к NoMethodError, потому что у него нет определения суперкласса, а переопределение метода теряет свое первоначальное определение. Так что alias_method_chain на самом деле является самым простым способом достижения super -подобного поведения в этой ситуации.

Иногда alias_method_chain излишне убивает ваше пространство имен и затрудняет отслеживание стека. Но иногда это лучший способ изменить поведение метода без потери исходного поведения. Вам просто нужно понять разницу, чтобы знать, что уместно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...