DEV Community

M Bellucci
M Bellucci

Posted on

7

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

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)