【问题标题】:Destroy has_many relation when updating associated record get empty更新关联记录时销毁has_many关系变空
【发布时间】:2019-09-23 09:17:28
【问题描述】:

我有以下型号:

class Course < ApplicationRecord
  belongs_to :super_course, inverse_of: :courses
  ...
end

class SuperCourse < ApplicationRecord
  has_many :courses, dependent: :nullify, inverse_of: :super_course
  ...
end

这两个模型如下相互关联:SuperCourse 将具有某些特定条件的大量 Courses 分组。问题是,在后台我可以更改CourseSuperCourse,并且我不想有空的SuperCourses(即没有任何Course 关联)。

我一直在尝试做的是向Course 模型添加一个after_update 回调,以便它检查以前的SuperCourse 现在是否没有关联任何Course,但我没有不知道这是否是最好的解决方案(AFAIK,不太推荐回调)。根据this very old thread 中的回复,我现在得到的是:

class Course < ApplicationRecord
  belongs_to :super_course, inverse_of: :courses

  after_update :destroy_empty_super_course

  ...

  private

  def destroy_empty_super_course
    id = super_course_id_changed? ? super_course_id_was : super_course_id
    super_course = SuperCourse.find(id)
    super_course.destroy if super_course.courses.empty?
  end
end

但是当我测试这个时,我什至没有得到我想要的。这是失败的rspec sn-p:

context "when super_course is updated" do
  let(:super_course) { create(:super_course, courses: []) }
  let(:course) { create(:course, super_course: super_course) }
  let(:new_super_course) { create(:super_course, courses: []) }
  let(:new_course) { create(:course, semester: course.semester, subject: course.subject, super_course: new_super_course) }

  subject { course.update!(super_course: new_super_course) }

  it "should remove old super_course" do
    expect { subject }.to change(SuperCourse, :count).by(-1)
  end
end

这是我最好的选择吗?如果是这样,我怎样才能使它工作?如果没有,最好的选择是什么?

提前谢谢大家!

【问题讨论】:

    标签: ruby-on-rails ruby activerecord


    【解决方案1】:

    我在这方面做了更多工作,并且由于我使用的是 Rails 5.1(感谢 https://stackoverflow.com/a/20657833/7023504),我得到了这个回调的工作代码:

    def destroy_empty_super_course
      id = saved_changes[:super_course_id]&.first
      return unless id
      super_course = SuperCourse.find(id)
      super_course.destroy if super_course.courses.empty?
    end
    

    在将let 更改为let! 后,测试开始通过!无论如何,我仍然想知道这是否是这种情况下的最佳解决方案,但至少我有一些工作:)

    【讨论】:

      【解决方案2】:

      回调是唯一可行的解​​决方案,尽管为了更优雅和与 Rails 风格一致,为此目的使用around 回调是很常见的。 你的版本失败的原因是因为 after_update 回调发生在更新提交之后,所以 *_changed?*_was 方法不知道保存之前记录的脏更改。要在保存完成之前的时间段内获取数据,可以使用回调 - 它允许执行生成块的保存,使脏更改在 yield 语句之前可用实际上是在保存之后和之后更新yield之后语句中的逻辑。

      def destroy_empty_super_course
        return unless super_course_id_changed?
      
        id = super_course_id_was
      
        yield
      
        super_course = SuperCourse.find(id)
        super_course.destroy if super_course.courses.empty?
      end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-26
        • 1970-01-01
        • 2017-11-05
        • 2010-10-20
        • 2015-06-01
        • 1970-01-01
        • 2014-01-22
        • 1970-01-01
        相关资源
        最近更新 更多