在Rails模型中删除一个关系

17 浏览
0 Comments

在Rails模型中删除一个关系

我正在使用Rails 5,我有一个名为\'dosages\'的一对多关系的ActiveRecord \'Report\'。\n如果在一个新的报告上:\n

report.dosages.size   # 返回0
report.dosages.build
report.dosages.size   # 1,正确
report.dosages.first.destroy
report.dosages.size  # 仍然是1!

\n我明白它将dosage设置为\'已删除\',但是否有可能将其从关系列表中实际删除?\n(我无法在稍后将报告保存到数据库)

0
0 Comments

在Rails模型中删除关系的原因是要保留报告对象(report),但是将剂量(dosage)从数据库中删除,并将其标记为“deleted”。如果这是您的用例,首先需要了解的是,报告对象将保留,无论剂量是否被删除,因为它们之间是一对多的关系(1-*)。然而,您需要了解deletedestroy之间的区别,stackoverflow社区在这里回答了这个问题:Difference between Destroy and Delete

作为额外的提示,可以了解一下Ruby中的shebang(!)方法以及何时使用它,例如delete!destroy!

不,我不想从数据库中删除任何内容。我希望在结束时report.dosages.size为0。我不明白为什么Rails会设置destroyed标志,而不是删除对象。

0
0 Comments

在Rails模型中删除关系的问题出现的原因是因为在调用destroy方法时,并没有将关系对象持久化到数据库中。解决方法是在调用build方法之后,需要调用save方法将关系对象持久化到数据库中,然后再调用destroy方法删除关系对象。

具体的解决方法如下:

report = Report.first
dosage = report.dosages.build
dosage.save
report.dosages.size   # 1
report.dosages.first.destroy
report.dosages.size  # 0

使用这个方法可以解决问题。同时,还可以参考ActiveRecord_Associations_CollectionProxy::delete的文档获取更多信息。

以下是一个关于该问题的实际示例:

你的场景:

2.4.0 :007 > r.dosages.build
 => #
2.4.0 :008 > r.dosages.size
 => 1
2.4.0 :009 > r.dosages.first.destroy
   (0.1ms)  begin transaction
   (0.0ms)  commit transaction
 => #
2.4.0 :010 > r.dosages.size
 => 1

我建议的解决方法:

2.4.0 :005 > report = Report.first
  Report Load (0.1ms)  SELECT  "reports".* FROM "reports" ORDER BY "reports"."id" ASC LIMIT ?  [["LIMIT", 1]]
 => #
2.4.0 :006 > report.dosages.count
   (0.1ms)  SELECT COUNT(*) FROM "dosages" WHERE "dosages"."report_id" = ?  [["report_id", 1]]
 => 0
2.4.0 :007 > dosage = report.dosages.build
 => #
2.4.0 :008 > dosage.save
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "dosages" ("created_at", "updated_at", "report_id") VALUES (?, ?, ?)  [["created_at", "2018-07-03 14:06:08.709323"], ["updated_at", "2018-07-03 14:06:08.709323"], ["report_id", 1]]
   (0.9ms)  commit transaction
 => true
2.4.0 :009 > report.dosages.size
   (0.2ms)  SELECT COUNT(*) FROM "dosages" WHERE "dosages"."report_id" = ?  [["report_id", 1]]
 => 1
2.4.0 :010 > report.dosages.first.destroy
  Dosage Load (0.2ms)  SELECT  "dosages".* FROM "dosages" WHERE "dosages"."report_id" = ? ORDER BY "dosages"."id" ASC LIMIT ?  [["report_id", 1], ["LIMIT", 1]]
   (0.0ms)  begin transaction
  SQL (0.3ms)  DELETE FROM "dosages" WHERE "dosages"."id" = ?  [["id", 1]]
   (0.8ms)  commit transaction
 => #
2.4.0 :011 > report.dosages.size
   (0.2ms)  SELECT COUNT(*) FROM "dosages" WHERE "dosages"."report_id" = ?  [["report_id", 1]]
 => 0

以上是解决该问题的方法。在这个示例中,关联关系被设置为:

dosage.rb
class Dosage < ApplicationRecord
  belongs_to :report
end
report.rb
class Report < ApplicationRecord
  has_many :dosages
end

同时还需要进行以下迁移:

class ManyDosagesToReport < ActiveRecord::Migration[5.0]
  def change
    add_column :dosages, :report_id, :integer
  end
end

实际上,如果你尝试对构建但尚未持久化的dosage对象进行持久化,Rails会报错:

RuntimeError: can't modify frozen Hash

这是由于destroyed属性造成的。

0