28

我有一个从 ActiveRecord 继承的对象,但它有一个未保存在数据库中的属性,例如:

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

我希望能够使用 ActiveModel Dirty 提供的“bar_changed?”等方法跟踪对“bar”的更改。问题是,当我尝试在此对象上实现 Dirty 时,如文档中所述,我收到错误,因为 ActiveRecord 和 ActiveModel 都已定义define_attribute_methods,但参数数量不同,因此尝试时出现错误调用define_attribute_methods [:bar].

我在 includedefine_attribute_methods之前尝试过别名ActiveModel::Dirty,但没有运气:我得到一个未定义的方法错误。

关于如何处理这个问题的任何想法?当然,我可以手动编写所需的方法,但我想知道是否可以通过将 ActiveModel 功能扩展到 ActiveRecord 未处理的属性来使用 Rails 模块。

4

4 回答 4

43

我正在使用该attribute_will_change!方法,并且事情似乎运行良好。

它是 中定义的私有方法active_model/dirty.rb,但 ActiveRecord 将它混合在所有模型中。

这就是我最终在我的模型类中实现的:

def bar
  @bar ||= init_bar
end
def bar=(value)
  attribute_will_change!('bar') if bar != value
  @bar = value
end
def bar_changed?
  changed.include?('bar')
end

init_bar方法仅用于初始化属性。您可能需要也可能不需要它。

我不需要指定任何其他方法(例如define_attribute_methods)或包含任何模块。您确实必须自己重新实现一些方法,但至少行为将与 ActiveModel 基本一致。

我承认我还没有彻底测试它,但到目前为止我还没有遇到任何问题。

于 2012-05-16T12:46:17.467 回答
3

ActiveRecord具有曾经从您的类中调用的#attribute方法()将允许ActiveModel::Dirty创建方法,例如bar_was,bar_changed?许多其他方法。

因此,您必须attribute :bar在从ActiveRecord(或ApplicationRecord最新版本的 Rails)扩展的任何类中调用,以便在bar.

编辑:请注意,这种方法不应与attr_accessor :bar

编辑2:另一个注意事项是用attribute(例如attribute :bar, :string)定义的未持久属性将在保存时被吹走。如果您需要 attrs 在保存后闲逛(就像我所做的那样),您实际上可以(小心地)与 混合attr_reader,如下所示:

attr_reader :bar
attribute :bar, :string

def bar=(val)
  super
  @bar = val
end
于 2019-01-09T15:58:42.200 回答
2

我想出了一个对我有用的解决方案......

将此文件另存为lib/active_record/nonpersisted_attribute_methods.rbhttps ://gist.github.com/4600209

然后你可以做这样的事情:

require 'active_record/nonpersisted_attribute_methods'
class Foo < ActiveRecord::Base
  include ActiveRecord::NonPersistedAttributeMethods
  define_nonpersisted_attribute_methods [:bar]
end

foo = Foo.new
foo.bar = 3
foo.bar_changed? # => true
foo.bar_was # => nil
foo.bar_change # => [nil, 3]
foo.changes[:bar] # => [nil, 3]

但是,当我们这样做时,看起来我们会收到警告:

DEPRECATION WARNING: You're trying to create an attribute `bar'. Writing arbitrary attributes on a model is deprecated. Please just use `attr_writer` etc.

所以我不知道这种方法是否会在 Rails 4 中中断或更难......

于 2013-01-23T00:25:08.327 回答
0

自己编写 bar= 方法并使用实例变量来跟踪更改。

def bar=(value)
  @bar_changed = true
  @bar = value
end

def bar_changed?
  if @bar_changed
    @bar_changed = false
    return true
  else
    return false
  end
end
于 2011-04-16T00:56:37.927 回答