2011-04-02 12 views
46

Tengo tres clases: Escuela, Cuenta y Administración.Validar antes de destruir

Escuela

has_many: administatorships

has_many: administradores,: a través de =>: administratorships

cuenta

has_many: administratorships

Administratorship

belongs_to :account 
belongs_to :school 

before_destroy :confirm_presence_of_alternate_administratorship_in_school 

protected 

def confirm_presence_of_alternate_administratorship_in_school 
    unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0 
     errors.add_to_base "The school must have at least one administrator" 
    end 
end 

Ahora, lo que me gustaría pasar es cuando llamo destroy en una instancia de Administratorship, para que se añada un error al modelo y evitar la destrucción del modelo. He eliminado la declaración de menos para ver si eso impedía que se agregara el error, no fue el caso. Parece que tener errores en el modelo no impide que ocurra la destrucción.

Así que mi pregunta es, ¿hay alguna manera de evitar que se produzca la destrucción utilizando validaciones? Me doy cuenta de que podría definir un método que se destruye solo si se cumple la condición anterior, pero parece que un enfoque de validación es una solución más elegante.

+0

posible duplicado de [¿Cómo puedo 'validar' en destroy in rails] (http: // stackoverflow.com/questions/123078/how-do-i-validate-on-destroy-in-rails) –

+0

¿No debería ser '> 1'? ¿No se ejecuta esta consulta * antes * de que ocurra la eliminación? – panzi

+0

@panzi es contar las administraciones que no tienen la identificación de cuenta de la actual administración – tanman

Respuesta

64

Si devuelve falso de ese método before_destroy, evitará la destrucción.

+2

Para el caso general en el que desea que el objeto esté en estado válido antes de destruirlo, puede hacer: 'before_destroy: valid?' – Pathogen

+1

Parece que aumenta 'LocalJumpError: return inesperado' en Rails 4.1 si se devuelve inmediatamente, consulte https: // github.com/rails/rails/issues/12981 '#Fix 1' –

14

Si devuelve false de su método de validación evitará que se destruya el registro.

Ejemplo:

def confirm_presence_of_alternate_administratorship_in_school 
    unless school.administrators.count(["administratorships.account_id != #{id}"]) > 0 
    # errors.add_to_base() is deprecated in Rails 3. Instead do... 
    errors.add(:base, "The school must have at least one administrator") 

    # this will prevent the object from getting destroyed 
    return false 
    end 
end 

Nota al margen: que estaba teniendo problemas con este mensaje de error no se está visualizando. La validación funcionaría y el objeto no se eliminaría, pero no habría ningún mensaje que me informara qué sucedió. La razón de esto fue que el controlador estaba redirigiendo a la vista de índice en lugar de representar la vista de eliminación (si hay un error al crear un nuevo usuario, por ejemplo, se representará: acción => 'nuevo'. En este caso hay sin eliminar vista). Cuando esto sucedió, la variable de instancia en la que se estableció el mensaje de error (en errors.add (: base, "message")) se está reiniciando, lo que destruye el error en el proceso.

+2

¿Hay alguna manera de resolver el problema que mencionaste en la nota lateral? –

+5

Para resolver el problema planteado en ** la nota al margen **, agregue algo como lo siguiente a la línea 'redirect_to' en la acción de destrucción del controlador relevante:' format.html {redirect_to products_url,: notice => "An ¡Ocurrió un error! # {@ Products.errors [: base] .to_s} "}'. Y luego, siempre que muestre mensajes flash en algún lugar de su aplicación (por ejemplo, application.html.erb), aparecerá. Consulte [esta guía] (http://guides.rubyonrails.org/action_controller_overview.html#the-flash) y [esta pregunta] (http://stackoverflow.com/q/9390778/664833). – user664833

1

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 añadido de lo que es trivial para ocultar/mostrar un botón de borrar en la interfaz de usuario

4

Esta es una respuesta Rails 5, si devuelve falso, dará una advertencia de desaprobación: "Devolver false en Active Record y las devoluciones de llamada del Modelo activo no detendrá implícitamente una cadena de devolución de llamada en Rails 5.1".

def confirm_presence_of_alternate_administratorship_in_school 
    return if school.administrators.count(["administratorships.account_id != #{id}"]) > 0 
    errors[:base] << 'The school must have at least one administrator' 
    throw :abort 
end 
+0

En Rails 5.1.4 No veo una advertencia de desactivación. –

Cuestiones relacionadas