¿Puedo configurar la eliminación en cascada en Rails?


88

Sé que esto probablemente esté en Internet en algún lugar, pero no puedo encontrar la respuesta aquí en Stackoverflow, así que pensé que podría aumentar un poco la base de conocimientos aquí.

Soy un novato en Ruby and Rails, pero mi empresa está invirtiendo bastante en eso, así que estoy tratando de conocerlo con un poco más de detalle.

Ha sido difícil para mí cambiar mi forma de pensar para diseñar una aplicación desde el "modelo" en lugar de desde la base de datos, así que estoy tratando de averiguar cómo haría todo el trabajo de diseño que he realizado clásicamente en la base de datos en el En su lugar, modelo de rieles.

Entonces, la tarea más reciente que me he encomendado es descubrir cómo configurar un modelo de base de datos Rails para realizar eliminaciones en cascada. ¿Hay una manera fácil de hacer esto? ¿O tendría que ir a MySql y configurar esto?

Respuestas:


103

también puede establecer la opción: dependiente en: delete_all. : delete_all emitirá una única declaración SQL para eliminar todos los registros secundarios. debido a esto, el uso de: delete_all puede brindarle un mejor rendimiento.

has_many :memberships, dependent: :delete_all

8
Tu explicación es confusa. Se utilizará una única instrucción SQL, pero no se llamará al método de destrucción para cada fila secundaria. Tienes que usar destroy_all para eso.
John Topley

@John: espero que las ediciones aclaren la confusión. gracias por señalar eso.
Mike Breen

26
Asegúrese de comprender la diferencia entre usar :delete_ally :destroypara esto. Ambos harán que las membresías secundarias (1 nivel para eliminar [cita requerida] y npara destruir (si sus hijos tienen destrucciones dependientes)) se eliminen de la base de datos, pero :destroycrearán una instancia de cada objeto secundario y ejecutarán las devoluciones de llamada primero, mientras :delete_allque ejecutarán directamente una Sentencia SQL DELETE en la base de datos. :destroyes más lento debido a eso, pero le permite tener devoluciones de llamada cuando se destruye un registro. Eludir rieles en un extremo y potencial instanciación n ^ x en el otro.
jstim

2
Sugiero también configurar claves externas de la base de datos. De esa forma, los registros se eliminan con una sola operación. Vea la respuesta a continuación que publiqué.
Hendrik

66

Sí, puedes, si estás usando una relación como has_many, haz esto

has_many :memberships, dependent: :destroy

Dan, supongo que mi siguiente pregunta es si ejecuto un comando db migrate, ¿eso realmente lo configurará en la base de datos? ¿O la cascada se maneja completamente por rieles?
matt_dev

Sí, se maneja mediante rieles. (Sin embargo, asegúrese de que siempre necesita eliminar todas las filas relacionadas).
Stein G. Strindhaug

@Matt: la línea has_many debe estar en su clase de modelo, la migración no lo agregará por usted.
Gareth

Prefiero esta solución porque también funciona si el modelo dependiente tiene otra relación has_many
tpei

25

Al contrario de la respuesta proporcionada, sugiero encarecidamente hacer esto también a nivel de base de datos. En caso de que tenga diferentes procesos o un entorno de subprocesos múltiples, podría suceder que los registros no se eliminen correctamente. Además, la clave externa de la base de datos hace que las cosas sean mucho más rápidas al eliminar muchos datos.

Como en la respuesta sugerida, haga esto:

has_many :memberships, dependent: :delete_all

Sin embargo, también asegúrese de configurar foreign_keyuna migración. De esa forma, la base de datos se encarga de eliminar los registros automáticamente.

Para anular los valores cuando se elimina una membresía, asumiendo que tiene un modelo de usuario:

add_foreign_key :users, :memberships, on_delete: :nullify

También puede eliminar todos los modelos siempre que se elimine una membresía

add_foreign_key :users, :memberships, on_delete: :cascade

Entonces, ¿puedo usar "has_many: memberships, dependiente:: delete_all" y "add_foreign_key: users,: memberships, on_delete:: cascade"? ¿Funcionará bien?
Rubycon

2
Ni siquiera necesitará configurar el delete_allen el modelo. La clave externa se encargará de eliminar todo correctamente por usted a nivel de base de datos.
Hendrik

3
Tengo curiosidad por saber qué sucede cuando haces ambas cosas. Parece que no debería tener un efecto negativo, pero ¿alguien ha tenido una mala experiencia con esta práctica de hacer tanto el nivel AR como el DB?
James Klein

1
El nivel de la base de datos es lo que estaba buscando. Esta debería ser la respuesta aceptada en mi opinión. Los otros parecen que solo funcionan si mis consultas se apegan a las operaciones estándar de ActiveRecord.
Brett Beatty

10

Solo tenga en cuenta que delete_all no ejecutará ninguna devolución de llamada (como before_destroy y after_destroy) en los registros secundarios.


6

Parece que este complemento podría brindarle lo que está buscando si desea que las eliminaciones en cascada se reflejen en la estructura real de la base de datos:

http://www.redhillonrails.org/foreign_key_migrations.html

El formato para usar esto en una migración sería algo como esto:

create_table :orders do |t|
  t.column :customer_id, :integer, :on_delete => :set_null, :on_update => :cascade
  ...
end

5
Ese enlace está muerto, pero esta es una alternativa más nueva: github.com/matthuhiggins/foreigner
gdelfino
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.