DEV Community

M Bellucci
M Bellucci

Posted on

Rails Dependent Destroy

Dependent Options

Given:

class User < ApplicationRecord
  has_many :posts, dependent: XXXXXXX
end
class Post < ApplicationRecord
  belongs_to :user
end
Enter fullscreen mode Exit fullscreen mode

Let's see what happens when using the existing dependent options.

destroy

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Load (1.1ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 1]]                                                                                                      
  Post Destroy (0.4ms)  DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 1]]                                                                                                                       
  Post Destroy (0.1ms)  DELETE FROM "posts" WHERE "posts"."id" = ?  [["id", 2]]                                                                                                                       
  User Destroy (0.1ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 1]]                                                                                                                       
  TRANSACTION (0.7ms)  commit transaction                                                                                                                                                             
=> #<User id: 1, name: "John", email: "some@email.com", created_at: "2021-04-10 13:39:08.099961000 +0000", updated_at: "2021-04-10 13:39:08.099961000 +0000"> 
Enter fullscreen mode Exit fullscreen mode

delete_all

:delete => u.destroy will call u.posts.delete (callbacks no executed)

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Destroy (0.6ms)  DELETE FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 1]]                                                                                                             
  User Destroy (0.1ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 1]]                                                                                                                       
  TRANSACTION (1.0ms)  commit transaction                                                                                                                                                             
=> #<User id: 1, name: "John", email: "some@email.com", created_at: "2021-04-10 13:17:09.423794000 +0000", updated_at: "2021-04-10 13:17:09.423794000 +0000">
Enter fullscreen mode Exit fullscreen mode

destroy_async

It is supposed to delete posts in an async job but for my case this was the behavior:

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Load (0.1ms)  SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = ?  [["user_id", 1]]                                                                                                      
  User Destroy (0.7ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 1]]                                                                                                                       
  TRANSACTION (0.5ms)  rollback transaction                                                                                                                                                           
Traceback (most recent call last):                                                                                                                                                                    
        1: from (irb):2                                                                                                                                                                               
ActiveRecord::InvalidForeignKey (SQLite3::ConstraintException: FOREIGN KEY constraint failed)  
Enter fullscreen mode Exit fullscreen mode

nullify

:nullify => user.destroy will try to user.posts.each { |p| p.user = nil } (callbacks no executed)
Will raise an error if column is not nullable

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Update All (0.6ms)  UPDATE "posts" SET "user_id" = ? WHERE "posts"."user_id" = ?  [["user_id", nil], ["user_id", 1]]                                                                           
  TRANSACTION (0.1ms)  rollback transaction                                                                                                                                                           
Traceback (most recent call last):                                                                                                                                                                    
        1: from (irb):2                                                                                                                                                                               
ActiveRecord::NotNullViolation (SQLite3::ConstraintException: NOT NULL constraint failed: posts.user_id) 
Enter fullscreen mode Exit fullscreen mode

restrict_with_exception

:restrict_with_exception => u.destroy will do raise ActiveRecord::DeleteRestrictionError if u.posts.any?

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Exists? (0.1ms)  SELECT 1 AS one FROM "posts" WHERE "posts"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 1]]                                                                              
  TRANSACTION (0.0ms)  rollback transaction                                                                                                                                                           
Traceback (most recent call last):                                                                                                                                                                    
        1: from (irb):2                                                                                                                                                                               
ActiveRecord::DeleteRestrictionError (Cannot delete record because of dependent posts) 
Enter fullscreen mode Exit fullscreen mode

:destroy => a.destroy will call a.bs.destroy_all

restrict_with_error

:restrict_with_error => causes an error to be added to the owner if there is an associated object

irb(main):002:0> u.destroy                                                                                                                                                                            
  TRANSACTION (0.1ms)  begin transaction                                                                                                                                                              
  Post Exists? (0.1ms)  SELECT 1 AS one FROM "posts" WHERE "posts"."user_id" = ? LIMIT ?  [["user_id", 1], ["LIMIT", 1]]                                                                              
  TRANSACTION (0.1ms)  rollback transaction                                                                                                                                                           
=> false                                                                                                                                                                                              
irb(main):003:0> u.errors                                                                                                                                                                             
=> #<ActiveModel::Errors:0x00007fc2b209d428 @base=#<User id: 1, name: "John", email: "some@email.com", created_at: "2021-04-10 13:17:09.423794000 +0000", updated_at: "2021-04-10 13:17:09.423794000 +
0000">, @errors=[#<ActiveModel::Error attribute=base, type=restrict_dependent_destroy.has_many, options={:record=>"posts"}>]> 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)