ActiveRecord关联在更新中记录之前保存

时间:2009-07-14 20:44:48

标签: ruby-on-rails activerecord virtual-attribute

我的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?

2 个答案:

答案 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的错误。