Это прекрасно работает, если у вас есть отношения has_one или assign_to. Но потерпел неудачу с помощью has_many или has_many.
У меня есть система тегов, которая использует отношение has_many: through. Ни одно из решений не дало мне того, куда мне нужно было идти, поэтому я нашел решение, которое может помочь другим. Это было проверено на Rails 3.2.
Настройка
Вот базовая версия моих моделей:
Местоположение объекта:
class Location < ActiveRecord::Base
has_many :city_taggables, :as => :city_taggable, :dependent => :destroy
has_many :city_tags, :through => :city_taggables
accepts_nested_attributes_for :city_tags, :reject_if => :all_blank, allow_destroy: true
end
Метка объектов
class CityTaggable < ActiveRecord::Base
belongs_to :city_tag
belongs_to :city_taggable, :polymorphic => true
end
class CityTag < ActiveRecord::Base
has_many :city_taggables, :dependent => :destroy
has_many :ads, :through => :city_taggables
end
Решение
Я действительно переопределил метод autosave_associated_recored_for следующим образом:
class Location < ActiveRecord::Base
private
def autosave_associated_records_for_city_tags
tags =[]
#For Each Tag
city_tags.each do |tag|
#Destroy Tag if set to _destroy
if tag._destroy
#remove tag from object don't destroy the tag
self.city_tags.delete(tag)
next
end
#Check if the tag we are saving is new (no ID passed)
if tag.new_record?
#Find existing tag or use new tag if not found
tag = CityTag.find_by_label(tag.label) || CityTag.create(label: tag.label)
else
#If tag being saved has an ID then it exists we want to see if the label has changed
#We find the record and compare explicitly, this saves us when we are removing tags.
existing = CityTag.find_by_id(tag.id)
if existing
#Tag labels are different so we want to find or create a new tag (rather than updating the exiting tag label)
if tag.label != existing.label
self.city_tags.delete(tag)
tag = CityTag.find_by_label(tag.label) || CityTag.create(label: tag.label)
end
else
#Looks like we are removing the tag and need to delete it from this object
self.city_tags.delete(tag)
next
end
end
tags << tag
end
#Iterate through tags and add to my Location unless they are already associated.
tags.each do |tag|
unless tag.in? self.city_tags
self.city_tags << tag
end
end
end
Приведенная выше реализация сохраняет, удаляет и изменяет теги так, как мне было нужно при использовании fields_for во вложенной форме. Я открыт для обратной связи, если есть способы упростить. Важно отметить, что я изменяю теги при изменении метки, а не обновляю метку.