Уничтожить отношение has_many при обновлении связанной записи становится пустым - PullRequest
0 голосов
/ 05 мая 2019

У меня есть следующие модели:

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

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

Эти две модели связаны друг с другом следующим образом: a SuperCourse группирует множество Course s, которые имеют некоторые особые условия.Дело в том, что в backoffice я могу изменить SuperCourse из Course, и я не хочу иметь пустые SuperCourse s (то есть, с которыми Course не связано).

Я пытался добавить обратный вызов after_update к модели Course, чтобы он проверял, не связан ли предыдущий SuperCourse с Course, но я неНе знаю, если это лучшее решение (AFAIK, обратные вызовы не совсем рекомендуется).После ответа в этом очень старом потоке вот что я получаю прямо сейчас:

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, который не работает:

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

Это мой лучший выбор?Если так, как я могу заставить это работать?Если нет, то какой вариант лучше?

Спасибо всем заранее!

Ответы [ 2 ]

0 голосов
/ 05 мая 2019

Callback - единственное жизнеспособное решение для этого, хотя, чтобы быть более элегантным и совместимым со стилем Rails, обычно используется вокруг callback для этих целей. Причина, по которой ваша версия не удалась, заключалась в том, что обратный вызов 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
0 голосов
/ 05 мая 2019

Я работал над этим немного больше, и поскольку я использую 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! тест начал проходить! Во всяком случае, я все еще хотел бы знать, является ли это лучшим решением для этого случая, но по крайней мере у меня что-то работает:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...