0

我有一个Entry有很多Tags 的模型。通过虚拟属性将Tags 添加到我的表单上的文本框中,从而将它们添加到条目中。tag_names在对Entry模型进行验证之前,tag_names 字符串Tag使用find_or_create_by_name. 该Tag模型还具有验证以确保标签名称与通过关联运行的正则表达式匹配。

我的 Entry 模型如下所示:

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

当我创建一个新Entry对象并为其分配标签时,一切正常 - 如果其中一个Tags 上存在验证错误,则不会保存标签,并且会传回错误消息。但是,如果我尝试使用无效标记更新现有的 Entry 对象,而不是传回消息,我的self.tags=调用(在update_tags上面)会抛出带有验证错误消息的异常。即使我覆盖find_or_create_by_name实际上只是返回一个新对象而不是调用create,我也会得到相同的结果。

在我看来(并且文档似乎证实了)该tags=调用实际上是在对象已经存在Tag时保存主记录之前保存我的对象。Entry我能做些什么来让这个保存不发生,或者阻止它引发异常并导致我的保存返回错误?

4

2 回答 2

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.

更新包含删除丢弃标签的代码。

于 2009-07-15T03:23:57.443 回答
1

您可以捕获引发的异常并返回 false,在这种情况下,update_tags将停止保存在Entry.

或者,如果您想避免处理该异常,您可以构建一个新的 Tag 实例,其中一个尚不存在,并在继续 ( new_tag.valid?) 之前检查它是否有效,如果不是,则返回 false from update_tags

于 2009-07-14T21:39:13.110 回答