How to "check" for destruction in rails

When destroying a backup resource, I want to guarantee a few things before I allow the destruction operation to continue? Basically, I want to stop the kill operation if I notice that this will cause the database to be invalid? There are no callbacks for checking for the kill operation, so how can I “confirm” whether to accept the kill operation?

+63
ruby callback ruby-on-rails
Sep 23 '08 at 19:04
source share
9 answers

You can create an exception, which you then catch. Rails wraps deletes transactions, which helps.

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 super end end 

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

 def before_destroy return true if booking_payments.count == 0 errors.add :base, "Cannot delete booking with payments" # or errors.add_to_base in Rails 2 false # Rails 5 throw(:abort) end 

myBooking.destroy will now return false, and myBooking.errors will be populated when it returns.

+53
23 Sep '08 at 19:22
source share

just a note:

For rails 3

 class Booking < ActiveRecord::Base before_destroy :booking_with_payments? private def booking_with_payments? errors.add(:base, "Cannot delete booking with payments") unless booking_payments.count == 0 errors.blank? #return false, to not destroy the element, otherwise, it will delete. end 
+46
Jul 29 2018-11-23T00:
source share

This is what I did with Rails 5:

 before_destroy do cannot_delete_with_qrcodes throw(:abort) if errors.present? end def cannot_delete_with_qrcodes errors.add(:base, 'Cannot delete shop with qrcodes') if qrcodes.any? end 
+11
Jun 17 '16 at 11:36
source share

For ActiveRecord associations, has_many and has_one enable a dependent option that ensures that related table rows are deleted when they are deleted, but this usually means that your database is clean and not preventing it from being invalidated.

+5
Sep 23 '08 at 22:16
source share

You can wrap the destroy action in the if statement in the controller:

 def destroy # in controller context if (model.valid_destroy?) model.destroy # if in model context, use `super` end end 

Where is valid_destroy? this is a method in your model class that returns true if the conditions for deleting the record are met.

The presence of this method will also allow you to prohibit the display of the delete option to the user, which will improve the user’s work, as the user will not be able to perform an illegal operation.

+5
Sep 24 '08 at 5:11
source share

I ended up using the code to create a can_destroy override on activerecord: https://gist.github.com/andhapp/1761098

 class ActiveRecord::Base def can_destroy? self.class.reflect_on_all_associations.all? do |assoc| assoc.options[:dependent] != :restrict || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?) end end end 

This has an additional advantage: make it trivial to hide / show the delete button on ui

+4
Jun 17 '13 at 19:14
source share

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

+3
Sep 23 '08 at 19:26
source share

I have these classes or models

 class Enterprise < AR::Base has_many :products before_destroy :enterprise_with_products? private def empresas_with_portafolios? self.portafolios.empty? end end class Product < AR::Base belongs_to :enterprises end 

Now that you are deleting an enterprise, this process checks to see if there are any products related to the enterprises. Note. You should write this at the top of the class to check it first.

+3
Jan 10 '14 at 23:00
source share

Use ActiveRecord context validation in Rails 5.

 class ApplicationRecord < ActiveRecord::Base before_destroy do throw :abort if invalid?(:destroy) end end 
 class Ticket < ApplicationRecord validate :validate_expires_on, on: :destroy def validate_expires_on errors.add :expires_on if expires_on > Time.now end end 
0
Dec 25 '17 at 12:52
source share



All Articles