Rails: how to overwrite :destroy method?
Asked Answered
C

3

8

I tried lot of things to overwrite the behavior of the :destroy method but nothing works. I used first the acts_as_paranoia plugin, but it doesn't work with a model in a has_many :through association.

I want to overwrite :destroy method just to do something like this:

  def destroy
    _run_destroy_callbacks { delete }
  end

  def delete    
    self.update_attribute(:status => 0)
    freeze
  end

That is, I just want to update another field (status to 0) instead of destroying the record itself.

Cymoid answered 17/2, 2012 at 18:3 Comment(0)
G
9

Have you tried?:

 class MyClass < ActiveRecord::Base
  def destroy
    update_attribute(:status, 0)
  end
 end

EDIT: Based on comments, there might be something else at work and it might just be the (:dependent=>'') designation on the association definition -- or if it's a HABTM, it might not work at all. Maybe this info on delete and destroy through associations will help? Pasted relevant section below:

Delete or destroy?

has_many and has_and_belongs_to_many associations have the methods destroy, delete, destroy_all and delete_all.

For has_and_belongs_to_many, delete and destroy are the same: they cause the records in the join table to be removed.

For has_many, destroy will always call the destroy method of the record(s) being removed so that callbacks are run. However delete will either do the deletion according to the strategy specified by the :dependent option, or if no :dependent option is given, then it will follow the default strategy. The default strategy is :nullify (set the foreign keys to nil), except for has_many :through, where the default strategy is delete_all (delete the join records, without running their callbacks).

Gpo answered 17/2, 2012 at 18:33 Comment(5)
Yes, it was the first thing I tried. This does nothing. I can see the DELETE FROM request in my logs. The custom destroy method seems to not be called.Cymoid
Hrm, I actually use this in several models in several apps and it works without fail. Is there anything 'special' with this particular model and are you calling delete or destroy? You'll want the latter.Gpo
The record is destroyed from its parent in an AREL (ActiveRecord relation), maybe it's the problem ? In fact, I update my wine model and record (main form, update method in controller), and if I choose to delete the child (the varietal model that contains the custom destroy), it's not called apparently. Maybe, the rails mechanism is different to destroy if it the parent that destroy the child (don't call destroy on the child model ?)Cymoid
hard to say actually, I added a snippet from the AR docs, maybe that will uncover something.Gpo
Thanks you for your help and suggestions. The relation between wine model and varietal model is "has_many through". I read your spinnet, I tried with "destroy, delete, delete_all"... but it's the same. I hope you understand that my problem is when I update the master model (wine) and not if I want to destroy it (and destroy child). For example, I created a wine (and add a varietal) then I want to update the wine (and choose to destroy the varietal added) => here is my problem, I shown the DELETE FROM varietals (instead of UPDATE to status=0) and the UPDATE WINE (OK)...Cymoid
C
0

As Miked said, this code was the good if we want to destroy the varietal "manually":

@varietal = Varietal.find('1')    
@varietal.destroy 

def destroy
  update_attribute(:status, 0)
end

This works perfectly. However, as I said, if we update the parent record, I didn't find the destroy/delete/delete_all method called on the child... So if someone has an idea...

Cymoid answered 20/2, 2012 at 9:31 Comment(0)
C
0

I think that the best way to do this is to overwrite the before_destroy: filter to manipulate the dependents:

def Model
    before_destroy: mark_as_deleted

    def mark_as_deleted  
        self.update_attribute(:status => 0)
    end
end

This wouldn't cancel the destroy though.

The full callback documentation is here.

Cyr answered 17/11, 2015 at 8:19 Comment(2)
This still calls destroy() in the controller, it just runs through your def first. Returning false doesn't work, at least not that I could tell.Stink
This answer is incorrect. If you call before_destroy the record will be deleted even after the update.Chiles

© 2022 - 2024 — McMap. All rights reserved.