我正在尝试关联现有记录,同时仍然可以添加新记录。以下不起作用,但非常接近我的需要。如何完成关联现有记录和创建新记录?
has_many :comments, :through => :commentings, :source => :commentable, :source_type => "Comment"
accepts_nested_attributes_for :comments, :allow_destroy => true
def autosave_associated_records_for_comments
comments.each do |comment|
if existing_comment = Comment.find_by_fax_and_name(comment.fax, comment.name)
self.comments.reject! { |hl| hl.fax == existing_comment.fax && hl.name == existing_comment.name }
self.comments << existing_comment
else
self.comments << comment
end
end
end
答案 0 :(得分:6)
我已经找到了解决方案,但如果您知道更好的方法,请告诉我们!
def autosave_associated_records_for_comments
existing_comments = []
new_comments = []
comments.each do |comment|
if existing_comment = Comment.find_by_fax_and_name(comment.fax, comment.name)
existing_comments << existing_comment
else
new_comments << comment
end
end
self.comments << new_comments + existing_comments
end
答案 1 :(得分:1)
我有一个使用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_record_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) || StateTag.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时,上述实现以我需要的方式保存,删除和更改标记。如果有简化方法,我愿意接受反馈。重要的是要指出,我在标签更改时明确更改标签,而不是更新标签标签。
答案 2 :(得分:0)
在 Rails 6(可能还有更早的版本)中,很容易覆盖 accepts_nested_attributes_for
生成的属性编写器并使用 find_or_initialize_by
。
在这种情况下,您可以简单地编写:
has_many :comments, :through => :commentings, :source => :commentable, :source_type => "Comment"
accepts_nested_attributes_for :comments, :allow_destroy => true
def comments_attributes=(hashes)
hashes.each { |attributes| comments << Comment.find_or_initialize_by(attributes)
end
在传递 :id
或 :_destroy
键时尚未对此进行测试,因为它不适用于我的情况,但如果您愿意,请随时分享您的想法或代码。
我很想看到 Rails 在本地实现这一点,也许是通过将 upsert: true
选项传递给 accepts_nested_attributes_for
。