18

我有一个应用程序,我的用户可以在其中拥有一组偏好。两者都存储为 ActiveRecord 模型,如下所示:

class User < AR::Base
   has_one :preference_set
end

class PreferenceSet < AR::Base
   belongs_to :user
end

我现在可以访问用户的首选项:

@u = User.first
@u.preference_set => #<PreferenceSet...>
@u.preference_set.play_sounds => true

但是,如果尚未创建首选项集,则此操作将失败,因为 @u.preference_set 将返回 nil,我将调用play_sounds.nil

我要存档的是 User.preference_set 总是返回一个 PreferenceSet 实例。我试过这样定义它:

class User < ..
   has_one :preference_set

   def preference_set
     preference_set || build_preference_set
   end
end

这导致 a 'Stack level too deep',因为它递归地调用自己。

我的问题是这样的:

如何确保@user.preference_set返回相应的preference_set-record,或者如果不存在,则构建一个新记录?

我知道我可以重命名我的关联(例如preference_set_real)并避免以这种方式进行递归调用,但为了我的应用程序的简单起见,我想保留命名。

谢谢!

4

2 回答 2

57

有一个优雅简单的形式:

class User < ApplicationRecord
  has_one :preference_set
  
  def preference_set
    super || build_preference_set
  end
end

ActiveRecord有意启用这种使用super来覆盖关联方法的行为。

于 2013-07-24T05:38:45.010 回答
30

最好的方法是在创建主记录时创建关联记录:

class User < ActiveRecord::Base
   has_one       :preference_set, :autosave => true
   before_create :build_preference_set
end

这将设置它,因此每当User创建 a 时, a 也是如此PreferenceSet。如果您需要使用参数初始化关联的记录,请调用不同的方法,在before_create该方法中调用build_preference_set(:my_options => "here")然后返回true

然后,您可以通过迭代任何没有 aPreferenceSet并通过调用构建一个来规范化所有现有记录#create_preference_set

如果您只想在PreferenceSet绝对需要时创建它,那么您可以执行以下操作:

class User < ActiveRecord::Base
   has_one :preference_set

   def preference_set_with_initialize
     preference_set_without_initialize || build_preference_set
   end

   alias_method_chain :preference_set, :initialize
end
于 2010-09-28T13:01:16.127 回答