【问题标题】:Rails/ActiveRecord has_one association behavior when setting child to "nil"将 child 设置为“nil”时的 Rails/ActiveRecord has_one 关联行为
【发布时间】:2017-01-10 19:37:23
【问题描述】:

我和我的同事注意到有关 ActiveRecord has_one 关联的一些东西感觉错误/危险并且似乎不容易记录。

假设我们有这样的设置:

class User < ApplicationRecord
  has_one :subscription
end

class Subscription < ApplicationRecord
  belongs_to :user
end

没有“依赖:销毁”

> user = User.create(name: "Bob")
> user.subscription = Subscription.create(provider: "Stripe")

> user.subscription = nil

   (0.1ms)  begin transaction
  SQL (0.4ms)  UPDATE "subscriptions" SET "user_id" = ?, "updated_at" = ? WHERE "subscriptions"."id" = ?  [["user_id", nil], ["updated_at", 2017-01-10 19:24:56 UTC], ["id", 2]]
   (2.0ms)  commit transaction

用“依赖:销毁”

class User < ApplicationRecord
  has_one :subscription, dependent: :destroy
end


> user = User.create(name: "Bob")
> user.subscription = Subscription.create(provider: "Stripe")

> user.subscription = nil

   (0.1ms)  begin transaction
    SQL (1.1ms)  DELETE FROM "subscriptions" WHERE "subscriptions"."id" = ?  [["id", 1]]
   (0.7ms)  commit transaction

这是预期的行为吗?实际上,我很惊讶在没有明确调用 savedestroyupdate 等的情况下进行 SQL 调用。我也很困惑为什么 dependant: destroy 在这种情况下会改变行为。

这是否应该发生,如果发生,是否记录在任何地方? dependant: destroy 版本似乎特别危险。

这是用 Rails 5 以及几个版本的 Rails 4 测试的。

【问题讨论】:

  • 正如其他人所说,这是预期行为或至少设计行为Source Reference for HasOneAssociation#replace。本质上,您正在用nil“替换”Subscription,如果options[:dependent] == :destroy,这会导致Subscription#destroy,否则它会使关系无效。 User#destroy 的处理方式非常相似 Source

标签: ruby-on-rails ruby activerecord


【解决方案1】:

来自guide

4.2.5 什么时候保存对象?

当您将对象分配给 has_one 关联时,该对象是 自动保存(为了更新它的外键)。此外, 任何被替换的对象也会自动保存,因为它的 外键也会改变。

如果这些保存中的任何一个由于验证错误而失败,则 赋值语句返回 false 并且赋值本身是 取消。

如果父对象(声明 has_one 关联的对象)是 未保存(即 new_record? 返回 true),则子对象为 没有保存。它们会在保存父对象时自动进行。

如果你想将一个对象分配给一个 has_one 关联而不 保存对象,使用 association.build 方法。

【讨论】:

    【解决方案2】:

    这是预期的行为吗?

    是的。来自Rails docs on has_one associations 的示例:

    Account#beneficiary=(beneficiary)(类似于 beneficiary.account_id = 帐户ID;受益人.保存)

    至于dependent: :destroy,来自同一个文档:

    :destroy 导致关联对象也被销毁

    因此 SQL DELETE 查询。

    【讨论】:

    • 您为dependent: destroy 引用的文档向我表明,当父级被销毁时,它的关联会随之而来(就像它一样)。它似乎并不暗示它会对直接更改关联对象的结果产生任何影响。我只是看错了吗?
    • 我接受了这个答案,因为解释 setter 如何为 has_one 工作的文档引用很有趣。但是,我个人将不得不相信dependent: destroy 的行为在这里很奇怪并且没有正确记录。嗯嗯。
    • 通读4.2.1.2 association=(associate) 和另一个答案,我认为你是对的,没有关于在将关联设置为nil 时触发DELETE SQL 查询的文档。它绝对是隐含的——与"When you assign an object to a has_one association, that object is automatically saved" 完全相反的效果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多