0

我有 2 个模型,User并且Stash.

一个 User 有很多 stash 和一个 default_stash。一个 Stash 有一个所有者(用户)。

在创建用户之前,我希望它构建一个 Stash 并将该 Stash 分配给 self.default_stash。按照此处的说明Rails - Best-Practice: How to createdependent has_one 关系我创建了 Stash 关系,但是我不能同时“创建”belongs_to 关系。

用户.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

存储.rb

belongs_to :owner, class_name: 'User'

此时可以找到 Stashuser.stashesuser.default_stash仍然为零,因为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块内,以进行验证等。

4

1 回答 1

1

我同意你的观点,你所描述的应该可以工作,但是如果你在内存中建立相关的记录并期望它们正确地保存在一起——所有的东西都连接起来——那么 ActiveRecord 对你如何定义它们特别挑剔。

但是您可以在不更改before_create.

通常的问题是,您需要向 AR 提示哪些关系彼此相反。has_manyandbelongs_to方法有一个选项:inverse_of。在您的情况下,问题是您有一个关系 ( Stash#owner) 的一侧实际上是另一侧 (User#stashesUser#default_stash) 的两个倒数,那么您将设置为什么inverse_offor Stash#owner

解决方案是在 Stash 中添加一个has_one(称其为owner_as_default),以平衡事情。然后您可以添加inverse_of到所有定义中,每个定义都在另一侧标识其逆。最终结果是这样的:

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

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

从那里,你before_create应该像你写的那样工作。

是的,这似乎是很多多余的定义。ActiveRecord 不能从外键中找出所有内容吗?我总是觉得我通过让一侧意识到它的反面来打破一些墙壁。也许在未来的 Rails 版本中,这将被解决。

于 2012-06-11T00:23:42.723 回答