我有2个模型,User
和Stash
。
用户有很多东西,还有一个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
块内的所有内容,以进行验证等。
答案 0 :(得分:1)
我同意你的描述应该工作,但是如果你在内存中构建相关记录并希望它们能够正确地保存在一起 - 所有东西都连接起来 - 那么ActiveRecord尤其如此关于你如何定义它们很挑剔。
但可以使其无效,而无需更改before_create
中的内容。
通常的问题是你需要给出关于哪些关系是彼此反转的AR提示。 has_many
和belongs_to
方法采用:inverse_of
选项。在你的情况下,问题是你有一个关系(Stash#owner
)的一面实际上是另一边的两个(User#stashes
和User#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版本中,这将被解决。