¿Cuál es la diferencia entre :destroy y :delete en Rails?
Tags: rails, ruby, spanish
Published: 2020-08-13 17:01:50

Una de las enseñanzas o instrucciones que se da cuando se diseñan bases de datos es que no debe haber información relacionada que no tenga su “dueño” o “padre”. Si un artículo tiene muchos comentarios y dicho artículo se elimina de la base de datos, tales comentarios también deben hacerlo.

En SQL a esto se le llama [borrado en cascada](https://www.techonthenet.com/sql_server/foreign_keys/foreign_delete.php). Para lograr esto en Rails usando ActiveRecord podemos usar la opción `dependent` y pasando algunas opciones disponibles.

A continuación trataré de explicar la diferencia entre las dos más comunes.

## `:destroy`

Se utiliza así:

```ruby
class User < ApplicationRecord
  has_many :payment_methods, dependent: :destroy
end
```

La opción `:destroy` le dice a ActiveRecord que elimine los registros hijos pero usando el método `destroy` de cada uno de ellos, es decir, primero los instancia y luego los elimina, uno por uno.

Si hay muchos registros hijos, será muy lento de esta forma.

Así luce mi consola al usar esta opción:

```ruby
User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  BEGIN
PaymentMethod Load (0.3ms)  SELECT "payment_methods".* FROM "payment_methods" WHERE "payment_methods"."user_id" = $1  [["user_id", 1]]
  SQL (24.7ms)  DELETE FROM "payment_methods" WHERE "payment_methods"."id" = $1  [["id", 3]]
  SQL (0.3ms)  DELETE FROM "payment_methods" WHERE "payment_methods"."id" = $1  [["id", 2]]
  SQL (0.2ms)  DELETE FROM "payment_methods" WHERE "payment_methods"."id" = $1  [["id", 1]]
  SQL (0.4ms)  DELETE FROM "users" WHERE "users"."id" = $1  [["id", 1]]
    (0.3ms)  COMMIT
```

## `:delete_all`

Se utiliza así:

```ruby
class User < ApplicationRecord
  has_many :payment_methods, dependent: :delete_all
end
```

En cambio, la opción `:delete_all` lo que hace es una eliminación directa mediante una consulta SQL pura. Más eficiente si hay muchos registros asociados a un padre.

Así luce mi consola cuando uso esta opción:

```ruby
User Load (0.4ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.1ms)  BEGIN
  SQL (0.3ms)  DELETE FROM "payment_methods" WHERE "payment_methods"."user_id" = $1  [["user_id", 3]]
  SQL (0.1ms)  DELETE FROM "users" WHERE "users"."id" = $1  [["id", 3]]
   (0.3ms)  COMMIT
```

Usar `:delete_all` no efectúa el llamado a cualquier callback definido en el modelo.

Así que hay varios puntos aquí para diferenciar estas dos opciones:

- `:destroy:` Instancia cada objeto. Llamado a _callbacks_. Menos eficiente.
- `:delete_all:` Borrado directo por SQL. No llama a _callbacks_. Más eficiente.

Dicha eficiencia depende de lo que se necesite hacer y es un costo aceptable en muchas ocasiones.

Cualquiera de los dos es correcto usar, ya cada caso particular determina cuál elegir al final.

### Enlaces

- [Documentación](http://guides.rubyonrails.org/association_basics.html#has-many-association-reference)
- [Explicado en Stack Overflow](http://stackoverflow.com/questions/2797339/rails-dependent-destroy-vs-dependent-delete-all)