从before_create挂钩中的内置has_many关联中检索默认的belongs_to记录?

时间:2012-06-10 23:44:33

标签: ruby-on-rails ruby ruby-on-rails-3 activerecord associations

我有2个模型,UserStash

用户有很多东西,还有一个default_stash。 Stash有一个所有者(用户)。

在创建用户之前,我希望它构建一个Stash并将Stash分配给self.default_stash。根据这里的说明Rails - Best-Practice: How to create dependent has_one relations我创建了Stash关系,但是我无法同时“创建”belongs_to关系。

User.rb

has_many :stashes, foreign_key: :owner_id
belongs_to :default_stash, class_name: 'Stash', foreign_key: :owner_id

before_create :build_default_stash
def build_default_stash
  self.default_stash = stashes.build
  true
end

Stash.rb

belongs_to :owner, class_name: 'User'

此时可以在user.stashes中找到Stash,但user.default_stash仍然为nil,因为stashes.build未在before_create块中返回id。

通过将以下内容添加到 User.rb

,可以实现我的目标
after_create :force_assign_default_stash
def force_assign_default_stash
  update_attribute(:default_stash, stashes.first)
end

但我更倾向于尽可能保留before_create块内的所有内容,以进行验证等。

1 个答案:

答案 0 :(得分:1)

我同意你的描述应该工作,但是如果你在内存中构建相关记录并希望它们能够正确地保存在一起 - 所有东西都连接起来 - 那么ActiveRecord尤其如此关于你如何定义它们很挑剔。

可以使其无效,而无需更改before_create中的内容。

通常的问题是你需要给出关于哪些关系是彼此反转的AR提示。 has_manybelongs_to方法采用:inverse_of选项。在你的情况下,问题是你有一个关系(Stash#owner)的一面实际上是另一边的两个(User#stashesUser#default_stash),所以你会设置什么作为inverse_of的{​​{1}}?

解决方案是向Stash添加Stash#owner(称之为has_one),以平衡问题。然后,您可以将owner_as_default添加到所有定义中,每个定义在另一侧标识其反转。最终结果如下:

inverse_of

(另外,您的belongs_to上不需要外键。)

从那里开始,class User < ActiveRecord::Base has_many :stashes, foreign_key: :owner_id, inverse_of: :owner belongs_to :default_stash, class_name: "Stash", inverse_of: :owner_as_default ... end class Stash < ActiveRecord::Base belongs_to :owner, class_name: "User", inverse_of: :stashes has_one :owner_as_default, class_name: "User", foreign_key: :default_stash_id, inverse_of: :default_stash end 应该按照你的写作工作。

是的,似乎这是一个多余的定义。 ActiveRecord无法从外键和诸如此类的东西中找到所有内容吗?我总是觉得我打破了一些墙,让一方知道这是什么反过来。也许在未来的Rails版本中,这将被解决。