【问题标题】:before_destroy is not firing from update_attributesbefore_destroy 没有从 update_attributes 触发
【发布时间】:2010-08-22 17:27:09
【问题描述】:

我有一个学生有很多课程。在 student#update 操作和表单中,我接受 course_ids 列表。当该列表更改时,我想调用某个函数。如果 update_attributes 创建了 course_student,我的代码确实会被调用,但如果 update_attributes 破坏 course_student,不会被调用。我可以让它触发,还是我必须自己检测更改?

# app/models/student.rb
class Student < ActiveRecord::Base
  belongs_to :teacher
  has_many :grades
  has_many :course_students, :dependent => :destroy
  has_many :courses, :through => :course_students
  has_many :course_efforts, :through => :course_efforts

  # Uncommenting this line has no effect:
  #accepts_nested_attributes_for :course_students, :allow_destroy => true

  #attr_accessible :first_name, :last_name, :email, :course_students_attributes

  validates_presence_of :first_name, :last_name
...
end

# app/models/course_student.rb
class CourseStudent < ActiveRecord::Base
  after_create  :reseed_queues
  before_destroy :reseed_queues

  belongs_to :course
  belongs_to :student

  private

  def reseed_queues
    logger.debug "******** attempting to reseed queues"
    self.course.course_efforts.each do |ce|
      ce.reseed
    end
  end

end

# app/controllers/students_controller.rb

  def update
    params[:student][:course_ids] ||= []
    respond_to do |format|
      if @student.update_attributes(params[:student])
        format.html { redirect_to(@student, :notice => 'Student was successfully updated.') }
        format.xml  { head :ok }
      else
        format.html { render :action => "edit" }
        format.xml  { render :xml => @student.errors, :status => :unprocessable_entity }
      end
    end
  end

【问题讨论】:

  • Rails 的哪个版本? Possibly Related Lighthouse discussion.
  • 谢谢,安迪。这是 Rails 3,beta 4。也许是时候试试 RC 了?
  • 我尝试将回调移动到 Student 模型,将 after_add 和 before_remove 回调放在 has_many course_students 关联中。同样的行为! after_add 触发,但 before_remove 不触发。
  • 当前解决方法:我检测到当前课程列表中的更改。每门更改的课程都会重新播种。这是检测线(对于我未来的自己):@student.courses.collect{|c| c.id.to_s}.to_set ^ params[:student][:course_ids].to_set

标签: ruby-on-rails collections associations callback


【解决方案1】:

事实证明,这种行为已记录在案,就在 has_many 方法中。来自 API 文档:

collection=objects 通过酌情删除和添加对象来替换集合内容。如果 :through 选项为 true,则除了销毁回调之外,连接模型中的回调将被触发,因为删除是直接的。

我不确定“因为直接删除”是什么意思,但我们已经知道了。

【讨论】:

    【解决方案2】:

    当使用 update/update_attributes 删除记录时,它会触发 delete 方法而不是 destroy

    @student.update_attributes(params[:student])

    Delete 方法 skips 回调,因此不会调用 after_create / before_destroy。 可以使用 accepts_nested_attributes_for 而不是这个,它会删除记录并支持回调。

    accepts_nested_attributes_for :courses, allow_destroy: true

    @student.update_attributes(courses_attributes: [ {id: student_course_association_id, _destroy: 1 } ])

    【讨论】:

      【解决方案3】:

      接受嵌套属性需要一个标志来触发嵌套销毁。至少它什么时候回来了。

      【讨论】:

      • 出色的接球,麦克莱恩。我很有希望,但改成这样: >>> class Student true
      • 只是为了好玩,因为实际上并没有任何 course_student 属性可以接受(只有 collection_ids=),所以我完全删除了accepts_nested_attributes_for。同样的行为。
      【解决方案4】:

      如果您添加dependent: :destroy,它将兑现这一点。请注意,如果您使用的是has_many through:,则需要同时添加该选项。

        has_many :direct_associations, dependent: :destroy, autosave: true
        has_many :indirect_associations, through: :direct_associations, dependent: :destroy
      

      (我在 Rails 3 中使用过,我敢打赌它也适用于 Rails 4)

      【讨论】:

      【解决方案5】:

      如果您的CourseStudent 指定belongs_to :student, :dependent =&gt; :destroy,那么如果没有学生,CourseStudent 记录似乎将无效。

      尝试关注我上面链接到的 LH 讨论,this one 我还尝试将 CourseStudent 中的 before_destroy 回调移动到 belongs_to 下方。链接的示例演示了回调的顺序与after_create 的关系,也许这同样适用于before_destroy。当然,既然您使用的是 Edge Rails,我也会尝试 RC,也许他们修复了一个错误。

      如果做不到这些,我会尝试制作一个非常简单的 Rails 应用程序,其中包含两个模型来演示问题并将其发布到 Rails 的 Lighthouse。

      【讨论】:

      • 在 student.rb 中,我添加了 :dependent => :destroy 到 has_many :course_students,所以如果一个学生被销毁, course_student 也会被销毁,而不是相反(尽管它没有任何一个都没有另一个存在是有意义的)。我还将 before_destroy 移动到 after :belongs_to,并且代码的行为相同。我还没有尝试过 RC,而且我还没有完全使用 edge Rails。我正在使用 RC4,因为 Heroku 目前支持它,而且我必须迁移几个开发人员。在我必须自己检测哪些项目被删除之前,我希望能得到一个简单的解决方法。
      • 我没有太多使用嵌套属性。 Dirty module stuff 是否适用于嵌套属性。我在想它可能会使您自己检测已删除的关联记录更容易。也许像 Parent.child.changed 这样的东西?在一个称为before_save 的方法中,如有必要,它会执行删除。只是到了这一点,还没有使用 Rails 3,并且一年多没有使用嵌套属性。 :(
      • 安迪,我删除了accept_nested_attributes_for,因为那里没有真正的属性;只是ID列表。代码行为相同。
      【解决方案6】:

      无法发表评论,所以我将添加一个答案条目。

      刚刚遇到同样的错误。经过几个小时试图解决这个问题和一个小时的谷歌搜索,我偶然发现了这个问题。连同链接的 LH 票证和 API 报价,它现在变得有意义了。谢谢!

      在谷歌搜索时,发现了一张旧票。直接链接不起作用,但谷歌缓存有一个副本。 只需在 Google 上查看 dev.rubyonrails.org/ticket/7743 的缓存版本

      似乎一个补丁从未进入 Rails。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-09
        • 2012-03-02
        • 1970-01-01
        • 1970-01-01
        • 2019-02-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多