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
Наконец, я нашел два решения:
добавить функцию обратного вызова в модели Team для ручного запуска функции обратного вызова
использовать
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