Comment puis-je “valider” sur détruire dans les rails

En cas de destruction d’une ressource reposante, je veux garantir quelques choses avant d’autoriser la poursuite d’une opération de destruction? Fondamentalement, je veux pouvoir arrêter l’opération de destruction si je remarque que cela placerait la firebase database dans un état invalide? Il n’y a pas de rappel de validation sur une opération de destruction, alors comment peut-on “valider” si une opération de destruction doit être acceptée?

Vous pouvez lever une exception que vous attrapez alors. Rails encapsule les suppressions dans une transaction, ce qui facilite les choses.

Par exemple:

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 

Vous pouvez également utiliser le rappel before_destroy. Ce rappel est normalement utilisé pour détruire des enregistrements dépendants, mais vous pouvez lancer une exception ou append une erreur à la place.

 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 va maintenant retourner false et myBooking.errors sera myBooking.errors lors du retour.

Juste une note:

Pour 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 

C’est ce que j’ai fait avec 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 

Les associations ActiveRecord has_many et has_one permettent une option dépendante qui s’assurera que les lignes de la table liée sont supprimées lors de la suppression, mais cela permet généralement de garder votre firebase database propre plutôt que de l’empêcher d’être invalide.

Vous pouvez envelopper l’action de destruction dans une instruction “if” dans le contrôleur:

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

valid_destroy? est une méthode de votre classe de modèle qui renvoie true si les conditions de destruction d’un enregistrement sont remplies.

Avoir une méthode comme celle-ci vous permettra également d’empêcher l’affichage de l’option de suppression pour l’utilisateur – ce qui améliorera l’expérience utilisateur car l’utilisateur ne pourra pas effectuer une opération illégale.

J’ai fini par utiliser du code d’ici pour créer un remplacement can_destroy sur 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] != :ressortingct || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?) end end end 

Cela a l’avantage supplémentaire de rendre sortingvial de cacher / afficher un bouton de suppression sur l’interface utilisateur.

Vous pouvez également utiliser le rappel before_destroy pour générer une exception.

J’ai ces classes ou modèles

 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 

Désormais, lorsque vous supprimez une entreprise, ce processus valide s'il existe des produits associés aux entresockets. Remarque: vous devez écrire ceci en haut de la classe pour le valider au préalable.

Utilisez la validation de contexte ActiveRecord dans 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 

J’espérais que cela serait supporté alors j’ai ouvert un problème de rails pour le faire append:

https://github.com/rails/rails/issues/32376