Rails: how to disable before_destroy callback when it's being destroyed because of the parent is being destroyed (:dependent => :destroy)

前端 未结 5 1576
伪装坚强ぢ
伪装坚强ぢ 2021-02-05 06:06

I have two classes: Parent and Child with

Child:

belongs_to :parent

and

Parent

has_many :children, :dependent          


        
5条回答
  •  渐次进展
    2021-02-05 06:46

    The accepted answer does not solve the original problem. Jose wanted 2 things:

    1) To ensure that the Parent always has at least one child

    and

    2) To be able to delete all children when the Parent is deleted

    You do not need any before_destroy callbacks to prevent the deletion of a child.

    I wrote a detailed blog post describing the solution, but I'll cover the basics here as well.

    The solution includes various ingredients: the use of presence validation and nested attributes in the Parent model, and making sure that the method that deletes the child doesn't call .destroy on the child, but that the child is deleted from the Parent model via nested attributes.

    In the Parent model:

    attr_accessible :children_attributes
    
    has_many :children, dependent: :destroy
    accepts_nested_attributes_for :children, allow_destroy: true
    validates :children, presence: true
    

    In the child model:

    belongs_to :parent
    

    Next, the easiest way to allow children to be deleted, except for the last one, is to use nested forms, as covered in Railscasts #196. Basically, you would have one form with fields for both the Parent and the Children. Any updates to the Location, as well as the Children, including the deletion of children, would be processed by the update action in the Parent Controller.

    The way you delete a child via nested forms is by passing in a key called _destroy with a value that evaluates to true. The allow_destroy: true option we set in the Parent model is what allows this. The documentation for Active Record Nested Attributes covers this, but here's a quick example that shows how you would delete a Child whose id equals 2 from its Parent:

    parent.children_attributes = { id: '2', _destroy: '1' }
    parent.save
    

    Note that you don't need to do this yourself in the Parent Controller if you're using nested forms as in Railscasts #196. Rails takes care of it for you.

    With the presence validation in the Parent model, Rails will automatically prevent the last child from being deleted.

    I think that at the time Jose posted his question, the presence validation was not working the way it was supposed to. It wasn't fixed until July of 2012 with this pull request, but that was almost 2 years ago. Seeing dbortz post his outdated solution 12 days ago made me realize that there is still confusion about this issue, so I wanted to make sure to post the correct solution.

    For an alternate solution that doesn't use nested forms, see my blog post: http://www.moncefbelyamani.com/rails-prevent-the-destruction-of-child-object-when-parent-requires-its-presence/

提交回复
热议问题