Eliminacion de registros relacionados en otras tablas

Hola de nuevo amigos!

Quisiera que me den una sugerencia a ver en donde y como pudo hacer una
validacion antes de eliminar un registro. En mi aplicación tengo una
tabla
alumnos que tiene un clva eforánea llamada nacionalidad_id y una tabla
de
nacionalidades entonces si trato de eliminar una nacionalidad cuyo id
este
en la tabla alumno me da un error de que se esta violando una
restriccion de
clave foránea en la tabla alumnos

he estado revisando la documentacion de Rails y no consigo de algun
validate
que me permita eso. Mi pregunta es: existe un validates que me pueda
hacer
eso automaticamente? o tengo que hacer en la clase modelo de alumnos un
validates aparte donde consulto si el id que voy a eliminar esta en la
tabla
alumno o hago un metodo en el controller alumno donde guardo en una
variable
@existe = Alumno.find_by_sql("select count(*) from alumnos where
nacionalidad_id = "+ params[:id])
luego pregunto si existe es igual a cero lo dejo borrar sino mando un
mensaje de error.

Cual es la mejor manera de hacerlo? en el modelo o en el controller?

quiero saber sugerencia de expertos en rails y el porque es mejor hacer
dicha validación en el modelo o en controller donde esta las reglas de
negocio?

muchas gracias

Algo como …

class Alumno < ActiveRecord::Base
belongs_to :nacionalidad
end

     class Nacionalidad < ActiveRecord::Base
       has_many :alumnos, :dependent => :nullify    # Si lo que

quieres es pasarlos a null
has_many :alumnos, :dependent => :destroy # Si quieres
eliminarlos …
end

… te sirve?

no amigo no me sirve no los quiero eliminar, si los quisiera eliminar ya
sabia que tenia que poner en el has_many :alumnos, :dependent =>
:destroy

lo que yo quiero es validar que si voy a eliminar una nacionalidad y
existe
al menos un alumno registrado con esa nacionalidad mandar una mensaje
de
error al usuario diciendole que no se puede eliminar la nacionalidad
porque
existen alumnos registrados con esa nacionalidad

no se si me captas la idea!

El día 6 de julio de 2008 12:49, Francesc E. <
[email protected]> escribió:

2008/7/5 Manuel P. [email protected]:

Utiliza before_destroy para hacer la comprobación, en el modelo
Nacionalidad.

Como dice la docu:

If a before_* callback returns false, all the later callbacks and the
associated action are cancelled.


Manuel, que
piensa que eres una excelente persona y medra en torno a
http://simplelogica.net y/o simplelogica.net
Recuerda comer mucha fruta y verdura.

Amigos, no escriban en listas de correo nada más levantar de la siesta
que pasa lo que pasa. Y lean, como siempre, los correos de Manuel :smiley:

On Sat, Jul 5, 2008 at 7:40 PM, Sergio Gil Pérez de la Manga
[email protected] wrote:

no se si me captas la idea!

Ojo lo he escrito aquí al vuelo eh?


Sergio Gil Pérez de la Manga
e-mail > [email protected]
blog > http://www.lacoctelera.com/porras


Sergio Gil Pérez de la Manga
e-mail > [email protected]
blog > http://www.lacoctelera.com/porras

On Sat, Jul 5, 2008 at 7:28 PM, Manuel P.
[email protected] wrote:

no amigo no me sirve no los quiero eliminar, si los quisiera eliminar ya
sabia que tenia que poner en el has_many :alumnos, :dependent => :destroy

lo que yo quiero es validar que si voy a eliminar una nacionalidad y existe
al menos un alumno registrado con esa nacionalidad mandar una mensaje de
error al usuario diciendole que no se puede eliminar la nacionalidad porque
existen alumnos registrados con esa nacionalidad

no se si me captas la idea!

Supongo que puedes hacerlo con algo como:

class Nacionalidad < AR::B
has_many :alumnos
validate :check_no_alumnos
def check_no_alumnos
errors.add_to_base(‘Hay alumnos de esta nacionalidad’) unless
alumnos.empty?
end
end

Ojo lo he escrito aquí al vuelo eh?


Sergio Gil Pérez de la Manga
e-mail > [email protected]
blog > http://www.lacoctelera.com/porras

vaya manuel esa si ni me la sabia… ahora en que parte del modelo de
nacionalidad debo colocar en before_destroy?
muchas gracias

2008/7/6 Manuel González Noriega [email protected]:

2008/7/5 Manuel P. [email protected]:

vaya manuel esa si ni me la sabia… ahora en que parte del modelo de
nacionalidad debo colocar en before_destroy?
muchas gracias

Échale un vistazo a toda la parte de callbacks de AR, que solucionan
muchos problemas. Ahí te explica cómo se definen y muchas otras cosas,
acompañado de ejemplos:


Manuel, que
piensa que eres una excelente persona y medra en torno a
http://simplelogica.net y/o simplelogica.net
Recuerda comer mucha fruta y verdura.

ya lo resolvi de la siguiente manera, les coloco el codigo para que
sirva de
soluci{on para futuras dudas en el mismo caso
en el controller de nacionalidad en el metodo destroy hice lo siguiente:

def destroy
@nacionalidad = Nacionalidad.find(params[:id])
@alumnos = Alumno.find(:all, :conditions => "nacionalidad_id = " +
id.to_s)

if @alumnos.empty?
       @nacionalidad.destroy
       flash[:notice] = 'El registro fue eliminado 

satisfactoriamente.’
else
flash[:notice] = ‘No se puede
eliminar el registro porque esta relacionado con otro(s)
registro(s)

end
respond_to do |format|
format.html { redirect_to(alumnos_url) }
format.xml { head :ok }
end
end

¿que les parece?

2008/7/6 Sergio Gil Pérez de la Manga [email protected]:

             flash[:notice] = '<font color="#FF0000"><b>No se puede

eliminar el registro porque esta relacionado con otro(s)
registro(s)’
¿que les parece?

No uses el tag por dios :slight_smile: !!! … Usa flash[:error] y dale
style por CSS despues.

Respecto de la lógica, yo la implementaría en el modelo con un
before_destroy callback y no en el controlador.

¡Falta Uno! - http://www.falta-uno.com.ar/
Ricardo M.

bueno he intendado hacerlo en el modelo con el before_destroy pero me
dan
muchos errores… de verdad no entiendo! ahora cual es la diferencia o
el
problema de hacerlo en el controller o en modelo?

2008/7/6 Ricardo M. [email protected]:

On Sat, Jul 5, 2008 at 3:55 PM, Manuel P.
[email protected] wrote:

bueno he intendado hacerlo en el modelo con el before_destroy pero me dan
muchos errores… de verdad no entiendo! ahora cual es la diferencia o el
problema de hacerlo en el controller o en modelo?

Andar, anda de las dos formas. Desde mi punto de vista, está mal
hacerlo en el controlador porque :
a) Estas delegando mal la responsabilidad de las clases (teoria
basica de POO y buenas practicas).
b) Corres el riesgo de tener que hacer c&p en otro lado del
programa y caer en los tipicos problemas de duplicacion de codigo.

Solo fue una opinión, no es que no se pueda por algo técnico o similar.

¡Falta Uno! - http://www.falta-uno.com.ar/
Ricardo M.

2008/7/5 Manuel P. [email protected]:

bueno he intendado hacerlo en el modelo con el before_destroy pero me dan
muchos errores… de verdad no entiendo! ahora cual es la diferencia o el
problema de hacerlo en el controller o en modelo?

Homework:

http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model

Para discusión posterior de los conceptos y errores concretos de
código, tenemos la lista

Y, como te dicen, nunca jamás utilices :wink:


Manuel, que
piensa que eres una excelente persona y medra en torno a
http://simplelogica.net y/o simplelogica.net
Recuerda comer mucha fruta y verdura.

Estas creando verificar_fk como un método de la clase, no de la instancia.
Además en el controlador estas usando el método mal dos veces :slight_smile: En
estas dos líneas:

@alumnos = Nacionalidad.verificar_fk
no te va a devolver nada porque no hay id de instancia con el que
comparar el nacionalidad_id de los alumnos

@nacionalidad.destroy
Eso llama al before_filter y ahí no encuentra el método verificar_fk
porque no es un método de instancia.

Para arreglarlo, deberias hacer:

  1. En vez de ‘def self.verificar_fk’ define el método como ‘def
    verificar_fk’
  2. Desde el controlador haz @alumnos = @nacionalidad.verificar_fk
  3. Echar un ojo a una guía de programación orientada a objetos(no
    necesariamente de ruby) si necesitas aclarar conceptos y diferencias
    entre clases, instancias, variables, etc…

Salu2
Juanjo

On Sat, Jul 5, 2008 at 9:52 PM, Manuel P.

ricardo me parece excelente tus recomendaciones y voy a seguirlas.
Entonces
fijate me fui al modelo y coloque esto

before_destroy :verificar_fk

def self.verificar_fk
existe = Alumno.count(:conditions => "nacionalidad_id = " +
self.id.to_s)
if existe.nil?
return false
else
return true
end
end

y en el controller tengo esto:

def destroy
@nacionalidad = Nacionalidad.find(params[:id])
@alumnos = Nacionalidad.verificar_fk

if @alumnos == false
       @nacionalidad.destroy
       flash[:notice] = 'El registro fue eliminado 

satisfactoriamente.’
else
flash[:error] = ‘No se puede eliminar el registro porque esta
relacionado con otro(s) registro(s)’
end
respond_to do |format|
format.html { redirect_to(alumnos_url) }
format.xml { head :ok }
end
end

pero me da este error y no consigo la solucion

undefined method `verificar_fk’ for #<Nacionalidad id: 20, desc:
“italiana”>

Estoy intentando hacer lo que tu me dijiste pero de verdad que lo estoy
viendo muy complicado, no se en que estoy errando

2008/7/6 Ricardo M. [email protected]:

Ya veo que has resuelto el problema, pero por si acaso te aparece otra
vez o quieres eliminar ese código específico de tus modelos hace mucho
tiempo hice un plugin para ese pequeño “olvido” de Rails:
http://ruido-blanco.net/blog/rails-plugin-dependent-protect-espanol/

Bai.

Gracias Juanjo por la explicacion ya resolvi el problema esto fue lo que
hice.
En el modelo hice lo que tu me dijiste

def verificar_fk
existe = Alumno.count(:conditions => "nacionalidad_id = " + id.to_s)
if existe == 0
return false
else
return true
end
end

Y en el controller hice lo siguiente:

def destroy
@nacionalidad = Nacionalidad.find(params[:id])
@alumnos = @nacionalidad.verificar_fk

if @alumnos == false          @nacionalidad.destroy
       flash[:notice] = 'El registro fue eliminado 

satisfactoriamente.’
else
flash[:error] = ‘No se puede eliminar el registro porque esta
relacionado con otro(s) registro(s)’
end
respond_to do |format|
format.html { redirect_to(alumnos_url) }
format.xml { head :ok }
end
end

y listo hice pruebas y me elimina la nacionalidad si no hay alumnos
registrados con dicha nacionalidad y si los hay no me permite eliminar
la
nacionalidad y por supuesto me sale en mensaje de error

Muchas Gracias nuevamente juanjo, Dios te bendiga…

El día 6 de julio de 2008 16:02, Juanjo Bazán [email protected]
escribió:

ejele daniel ese plugin esta super buenisimo… asi uno se evita estar
haciendo codigo adicional en las clases modelos jejeje

Muchas gracias men!

El día 6 de julio de 2008 11:47, Daniel R. Troitiño <
[email protected]> escribió:

Manuel saludos…

En el modelo puedes hacer algo así:

has_many :alumnos
before_destroy :verificar_alumnos

private
def verificar_alumnos
if alumnos.any?
errors.add_to_base ‘Existen alumnos… bla bla’
return false
end
end

Y en el controlador:

def destroy
@nacionalidad = Nacionalidad.find params[:id]
flash[:notice] = ‘Registro eliminado…’ if @nacionalidad.destroy

respond_to do |format|

end
end

y limpias un poco el controlador de esta manera, ya que el método
verificar_alumnos es invocado automáticamente antes de eliminar el
registro. El manesaje de error es asignado automáticamente por el mismo
modelo, así que no es necesario colocarlo en el controlador tampoco.

En la vista el mensaje de error lo puedes obtener mediante el helper
error_messages_for

Saludos…

hola ricardo gracias por tu ayuda… de verdad que rails es demasiado
poderoso por eso me encanta, fijate que esa solucion que me estas dando
se
ve muchisimo mas limpia y mas entendible… voy a probar tambien como
tu me
acabas de indicar a ver como me va…
aunque con el plugin que me dijo daniel me esta interesando mas porque
asi
no escribo tanto en las clase modelo porque en mi caso cuando voy a
eliminar
una profesor tengo que verificar muchas tablas (cursos, horarios,
salones y
otras cosas por alli…)
pero vale tu solucion es muy buena y mas didáctica

Muchas Gracias

El día 6 de julio de 2008 14:55, Ricardo Rodríguez <
[email protected]> escribió: