views:

2896

answers:

4

On destruction of a restful resource, I want to guarantee a few things before I allow a destroy operation to continue? Basically, I want the ability to stop the destroy operation if I note that doing so would place the database in a invalid state? There are no validation callbacks on a destroy operation, so how does one "validate" whether a destroy operation should be accepted?

+7  A: 

You can raise an exception which you then catch. Rails wraps deletes in a transaction, which helps matters.

For example:

class Booking < ActiveRecord::Base
has_many   :booking_payments
....
def destroy
  raise "Cannot delete booking with payments" unless booking_payments.count == 0
  ... ok, go ahead and destroy

Alternatively you can use the before_destroy callback. This callback is normally used to destroy dependent records, but you can throw an exception or add an error instead.

def before_destroy
    errors.add_to_base "Cannot delete booking with payments" unless booking_payments.count == 0

myBooking.destroy will now return false, and myBooking.errors will be populated on return.

Airsource Ltd
Um, what's wrong with just checking the booking_payments association that should be defined on that model instead of calling BookingPayment.count, leading to ugly code?
Ryan Bigg
I've edited my reply accordingly. Sorry, I pulled this out of some old code...
Airsource Ltd
Note that where it now says "... ok, go ahead and destroy", you need to put "super", so the original destroy method is actually called.
Alexander Malfait
I don't think that the callback method works anymore.
Jaryl
+3  A: 

You can also use the before_destroy callback to raise an exception.

MattW.
+3  A: 

The ActiveRecord associations has_many and has_one allows for a dependent option that will make sure related table rows are deleted on delete, but this is usually to keep your database clean rather than preventing it from being invalid.

In an unrelated issue, I just figured out that "\_" is the correct syntax in markdown for making an underscore. Underscores usually italicize and bold things.

go minimal
+1 for "\_" didn't realize that was an option on SO
ahsteele
+2  A: 

You can wrap the destroy action in an "if" statement:

def destroy
  if (model.valid_destroy?)
    model.destroy
  end
end

Where *valid_destroy?* is a method on your model class that returns true if the conditions for destroying a record are met.

Having a method like this will also let you prevent the display of the delete option to the user - which will improve the user experience as the user won't be able to perform an illegal operation.

Toby Hede