rails example create callbacks before_update before association after_create after active ruby-on-rails ruby callback

ruby-on-rails - example - rails after action



¿Cómo ''validaré'' la destrucción en rieles? (10)

Sobre la destrucción de un recurso relajante, quiero garantizar algunas cosas antes de permitir que continúe una operación de destrucción. Básicamente, quiero la capacidad de detener la operación de destrucción si noto que al hacerlo colocaría la base de datos en un estado inválido. No hay devoluciones de llamada de validación en una operación de destrucción, entonces, ¿cómo se puede "validar" si se debe aceptar una operación de destrucción?


Es lo que hice con 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



Las asociaciones de ActiveRecord has_many y has_one permiten una opción dependiente que garantizará que las filas de la tabla relacionadas se eliminen en delete, pero esto generalmente es para mantener su base de datos limpia en lugar de evitar que no sea válida.


Puede envolver la acción de destrucción en una instrucción "if" en el controlador:

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

Donde valid_destroy? es un método en su clase de modelo que devuelve verdadero si se cumplen las condiciones para destruir un registro.

Tener un método como este también le permitirá evitar la visualización de la opción de eliminación para el usuario, lo que mejorará la experiencia del usuario ya que el usuario no podrá realizar una operación ilegal.


Puedes presentar una excepción que luego atrapes. Rails wraps elimina en una transacción, lo que ayuda a las cosas.

Por ejemplo:

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

Alternativamente, puede usar la devolución de llamada before_destroy. Esta devolución de llamada normalmente se usa para destruir registros dependientes, pero puede lanzar una excepción o agregar un error en su lugar.

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 ahora devolverá false y myBooking.errors se completará a la vuelta.


También puede usar la devolución de llamada before_destroy para generar una excepción.


Tengo estas clases o modelos

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

Ahora, cuando elimina una empresa, este proceso valida si hay productos asociados con empresas. Nota: Debe escribir esto en la parte superior de la clase para validarlo primero.


Terminé usando el código de aquí para crear una anulación can_destroy en 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

Esto tiene el beneficio adicional de hacer que sea trivial ocultar / mostrar un botón de eliminar en la interfaz de usuario


Utilice la validación de contexto de ActiveRecord en 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


solo una nota:

Para rieles 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