¿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?

matt_dev
fuente

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
Mike Breen
fuente
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
danmayer
fuente
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
Hendrik
fuente
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.

Jarin Udom
fuente
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
Sean McMains
fuente
5
Ese enlace está muerto, pero esta es una alternativa más nueva: github.com/matthuhiggins/foreigner
gdelfino