Rails4: Как вызвать обратный вызов для attr_accessor при использовании accepts_nested_attributes_for

Пожалуйста, проверьте псевдокод:

class Team
  has_many :users
  accepts_nested_attributes_for :users, allow_destroy: true
end

class User
  belongs_to :team
  has_many :addresses
  accepts_nested_attributes_for :addresses
  attr_accessor :dummy

  before_validation :generate_addresses_attributes
  def generate_addresses_attributes
    # Use the dummy value to set the addresses_attributes
  end
end

Теперь, когда выполнить team.update(users_attributes: [{"0" => { dummy: "changed!" }}])(другие поля не изменятся, кроме атрибута dummy), он не будет вызывать обратный вызов #generate_addresses_attributes, так как он думает, что ничего не меняется, нет сохранения, нет обратного вызова...

Поэтому мой вопрос заключается в том, как вызвать обратный вызов для виртуальных атрибутов или, возможно, принудительно сохранить для acceptpts_nested_attributes_for.

Спасибо!

2 ответа

Я обнаружил, что для Rails 5.1+ attribute работает лучше чем attr_accessor для этого варианта использования.

attribute загрязняет объект, тем самым вызывая обратные вызовы при его сохранении.

class User
  belongs_to :team
  has_many :addresses
  accepts_nested_attributes_for :addresses
  attribute :dummy, :string

  before_validation :generate_addresses_attributes
  def generate_addresses_attributes
    # Use the dummy value to set the addresses_attributes
  end
end

Наконец, я нашел два решения:

  1. добавить функцию обратного вызова в модели Team для ручного запуска функции обратного вызова

  2. использовать attribute_will_change! переопределить метод установки:

    class User
      belongs_to :team
      has_many :addresses
      accepts_nested_attributes_for :addresses
      attr_accessor :dummy
    
      def dummy=(value)
        attribute_will_change!("dummy") if @dummy != value
        @dummy = value
      end
    
      ...
    end
    

Мне пришлось внести некоторые изменения, чтобы это работало с Rails 5.1.3. Если имя attr_accessor (attr_accessor :dummy) и метод установки (def dummy=(value)), есть предупреждение об устаревании, предлагающее использовать attribute :dummy вместо этого и код не работал, как предполагалось. работает изменение кода следующим образом:

class User
  belongs_to :team
  has_many :addresses
  accepts_nested_attributes_for :addresses
  attr_accessor :dummy

  def dummy_attr=(value)
    attribute_will_change!(:dummy)
    self.dummy = value
  end    
  ...
end

:dummy_attr следует использовать в сильных параметрах вместо :dummy

Другие вопросы по тегам