我的Entry
模型有很多Tag
个。通过Tag
虚拟属性将tag_names
输入到表单上的文本框中,将Entry
添加到条目中。在Tag
模型上进行验证之前,使用find_or_create_by_name
将tag_names字符串转换为实际的Tag
对象。 class Entry < ActiveRecord::Base
has_many :entry_tags
has_many :tags, :through => :entry_tags
before_validation :update_tags
attr_writer :tag_names
private
def update_tags
if @tag_names
self.tags = @tag_names.split(",").uniq.map do |name|
Tag.find_or_create_by_name(name.strip)
end
end
end
end
模型还进行了验证,以确保标记名称与通过关联运行的正则表达式匹配。
My Entry模型如下所示:
Entry
当我创建一个新的Tag
对象并为其分配标记时,一切正常 - 如果其中一个self.tags=
上存在验证错误,则不会保存标记,并显示错误消息被传回来了。但是,如果我尝试使用无效标记更新现有Entry对象,而不是传回消息,则update_tags
调用(在上面的find_or_create_by_name
中)会抛出带有验证错误消息的异常。即使我覆盖create
实际上只是返回一个新对象而不是调用tags=
,我也会得到相同的结果。
在我{({3}}似乎证实)Tag
调用实际上是在Entry
保存主记录之前保存我的{{1}}对象时对象已经存在。我有什么办法可以让这个保存没有发生,或者阻止它引发异常而只是让我的保存返回false?
答案 0 :(得分:2)
我会尝试这样的事情:
class Entry < ActiveRecord::Base
has_many :entry_tags
has_many :tags, :through => :entry_tags
before_validation :update_tags
attr_writer :tag_names
validates_associated :tags
private
def update_tags
return unless @tag_names
current_tag_names = tags.map(&:name)
user_tag_names = @tag_names.split(",").uniq
#add in new tags
user_tag_names.each do |name|
next if current_tag_names.include?(name)
tags.build :name => name
end
#remove dropped tags
( current_tag_names - user_tag_names ).each do |name|
removed_tag = tags.find_by_name(name)
tags.delete(removed_tag)
end
end
end
这样您只需在update_tags
操作中初始化相关模型,因此不会抛出验证错误。我还添加了validates_associated :tags
,以便可以使用error_messages_for :entry
通过标准输入表格报告这些相关模型的错误。
更新包含用于删除已删除标记的代码。
答案 1 :(得分:1)
您可以捕获引发的异常并返回false,在这种情况下,从update_tags
返回false,这将暂停Entry
上的保存。
或者,如果您想避免处理该异常,您可以构建一个尚未存在的新Tag实例,并在继续(new_tag.valid?
)之前检查它是否有效,如果不是则返回来自update_tags
的错误。