扶手:创建关联,如果没有被发现,避免零差错

问题描述 投票:13回答:2

我有一个应用程序,我的用户可以有一组偏好。两者都如下存储的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

这是造成'Stack level too deep',因为它是递归调用本身。

我的问题是这样的:

我怎样才能确保@user.preference_set要么返回相应preference_set记录或者,如果不存在,建立一个新的?

我知道我可以只重命名我的协会(如preference_set_real),并避免递归调用此方法,但在我的应用程序简便起见,我想保持命名。

谢谢!

ruby-on-rails activerecord has-one
2个回答
28
投票

那么要做到这一点的最好办法是,当你创建主之一创建相关记录:

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

这将设置它,以便每当创建User,所以是一个PreferenceSet。如果您需要初始化带参数的相关记录,然后调用before_create这就要求build_preference_set(:my_options => "here")不同的方法,然后返回true

然后,您可以仅仅通过遍历任何没有PreferenceSet,并通过调用#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

43
投票

或者干脆

class User < ApplicationRecord
   has_one :preference_set

   def preference_set
     super || build_preference_set
   end
end

这工作,因为ActiveRecord的定义一个mixin的关联方法。

© www.soinside.com 2019 - 2024. All rights reserved.