what is dependent :destroy
Dependent is an option of Rails collection association declaration to cascade the delete action. The :destroy is to cause the associated object to also be destroyed when its owner is destroyed.
Code Preparation
First, the DB shemas and Rails Model should be ready for the following experiment.
rails g model books
rails g model authors
rails g model comments
rails g model authors_books
rails g migration CreateJoinTableBooksAuthors books authors
Migrations
The migration files should be looked like as listed.
# db/migrate/2019_create_join_table_books_authors.rb
class CreateJoinTableBooksAuthors < ActiveRecord::Migration[6.0]
def change
create_table :authors_books do |t|
t.integer "book_id", null: false
t.integer "author_id", null: false
t.index [:book_id, :author_id], unique: true
t.index [:author_id, :book_id]
t.timestamps
end
end
end
# db/migrate/2019_create_books.rb
class CreateBooks < ActiveRecord::Migration[6.0]
def change
create_table :books do |t|
t.string :name, null: false
t.timestamps
end
end
end
# db/migrate/2019_create_authors.rb
class CreateAuthors < ActiveRecord::Migration[6.0]
def change
create_table :authors do |t|
t.string :name, index: { unique: true }, null: false
t.timestamps
end
end
end
# db/migrate/2019_create_comments.rb
class CreateComments < ActiveRecord::Migration[6.0]
def change
create_table :comments do |t|
t.integer :book_id, null: false
t.string :content, null: false
t.timestamps
end
end
end
# db/migrate/2019_create_details.rb
class CreateDetails < ActiveRecord::Migration[6.0]
def change
create_table :details do |t|
t.integer :book_id, null: false
t.integer :paperback, null: false
t.string :publisher, null: false
t.timestamps
end
end
end
Run the db migration commands
rails db:create
rails db:migrate
Models
Then update the model files for querying.
# app/models/author.rb
class Author < ApplicationRecord
has_many :authors_books
has_many :books, through: :authors_books, dependent: :destroy
end
# app/models/authors_book.rb
class AuthorsBook < ApplicationRecord
belongs_to :author
belongs_to :book
validates :author, :book, presence: true
end
# app/models/book.rb
class Book < ApplicationRecord
has_many :comments, dependent: :destroy
has_many :authors_books
has_many :authors, through: :authors_books, dependent: :destroy
has_one :detail, dependent: :destroy
end
# app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :book
end
# app/models/detail.rb
class Detail < ApplicationRecord
belongs_to :book, dependent: :destroy
end
How Dependent :destroy Working
One-to-One Association
Has_one & belongs_to
book = Book.create name: Faker::Book.title
Detail.create book_id: book.id, paperback: rand(330..2_000), publisher: Faker::Company.name
book.destroy
book.detail.destroy
If you want to keep the strict one to one relations betweens books and details, you can add dependent: :destroy on both sides to remove the combined records. On contract, the book record won't be deleted when the detail record is destroyed.
One-to-Many Association
Has_many & belongs to
book = Book.create name: Faker::Book.title
3.times { Comment.create content: Faker::Quotes::Shakespeare.hamlet_quote, book_id: book.id }
book.comments
# book.destroy
# book.comments.first.destroy
Books
ID | NAME | CREATED_AT | UPDATED_AT |
---|---|---|---|
3 | Arms and the Man | 2019-06-24 14:05:05 | 2019-06-24 14:05:05 |
Comments
ID | BOOK_ID | CONTENT | CREATED_AT | UPDATED_AT |
---|---|---|---|---|
4 | 3 | A little more than kin, and... | 2019-06-24 14:05:05 | 2019-06-24 14:05:05 |
5 | 3 | This above all: to thine ow... | 2019-06-24 14:05:05 | 2019-06-24 14:05:05 |
6 | 3 | The lady doth protest too m... | 2019-06-24 14:05:05 | 2019-06-24 14:05:05 |
What happens when the owner record is deleted
If the owner record is delete, all the assocated records will be deleted immediately. Such as, if the a book row is delete in the books table, all the related coments will be delete as well. But, if a comment is deleted, the related book row won't be delete.
# has_many :comments
book = Book.create name: Faker::Book.title
3.times { Comment.create content: Faker::Quotes::Shakespeare.hamlet_quote, book_id: book.id }
# Book.first.destroy!
irb(main):012:0> Book.first.destroy!
Book Load (0.2ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? [["LIMIT", 1]]
(0.1ms) begin transaction
Author Load (0.2ms) SELECT "authors".* FROM "authors" INNER JOIN "authors_books" ON "authors"."id" = "authors_books"."author_id" WHERE "authors_books"."book_id" = ? [["book_id", 1]]
Book Destroy (1.4ms) DELETE FROM "books" WHERE "books"."id" = ? [["id", 1]]
(0.8ms) commit transaction
=> #<Book id: 1, name: "A Farewell to Arms", created_at: "2019-06-14 09:28:35", updated_at: "2019-06-14 09:28:35">
has_many :comments, dependent: :destroy
Book.first.comments
ID | BOOK_ID | CONTENT | CREATED_AT | UPDATED_AT |
---|---|---|---|---|
13 | 6 | There is nothing either goo... | 2019-06-24 14:10:24 | 2019-06-24 14:10:24 |
14 | 6 | Though this be madness, yet... | 2019-06-24 14:10:24 | 2019-06-24 14:10:24 |
15 | 6 | This above all: to thine ow... | 2019-06-24 14:10:24 | 2019-06-24 14:10:24 |
Book.first.destroy!
(0.1ms) SELECT sqlite_version(*)
Book Load (0.1ms) SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT ? OFFSET ? [["LIMIT", 1], ["OFFSET", 1]]
(0.1ms) begin transaction
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."book_id" = ? [["book_id", 4]]
Author Load (0.1ms) SELECT "authors".* FROM "authors" INNER JOIN "authors_books" ON "authors"."id" = "authors_books"."author_id" WHERE "authors_books"."book_id" = ? [["book_id", 4]]
Book Destroy (0.2ms) DELETE FROM "books" WHERE "books"."id" = ? [["id", 4]]
(0.9ms) commit transaction
=> #<Book id: 4, name: "Blood's a Rover", created_at: "2019-06-14 09:28:38", updated_at: "2019-06-14 09:28:38">
Many-to-Many Association
Has_many through a Pivot Table
Mock Data
Author.delete_all
Book.delete_all
Author.create name: Faker::Book.author
Author.create name: Faker::Book.author
4.times { Book.create name: Faker::Book.title }
Author.first.books << Book.first
Author.first.books << Book.second
Author.second.books << Book.third
Author.second.books << Book.fourth
Author
ID | NAME | CREATED_AT | UPDATED_AT |
---|---|---|---|
1 | Mrs. Willard Balistreri | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
Books
ID | NAME | CREATED_AT | UPDATED_AT |
---|---|---|---|
7 | Fear and Trembling | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
8 | The Proper Study | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
9 | After Many a Summer Dies th... | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
10 | Tirra Lirra by the River | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
AuthorsBooks
ID | BOOK_ID | AUTHOR_ID | CREATED_AT | UPDATED_AT |
---|---|---|---|---|
1 | 7 | 1 | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
2 | 8 | 1 | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
3 | 9 | 2 | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
4 | 10 | 2 | 2019-06-24 14:11:17 | 2019-06-24 14:11:17 |
Delete
Author.second.books
Author.first.authors_books
Author.first.books.first.destroy
# AuthorsBook.find(14).destroy
Removing records from the pivot table , only applied on that pivot table, no assocated records will be delete. In the case, authors and books won't be deleted when deleting authors_books rows.
If a assocated record is delete, the relation rows in that pivot table will also be deleted and all these operations are wrapped in a transaction.
Conclusion
In one -to-one, and one-to-many scenarios, has_one or has_many, the associated records will be deleted if the owner record got deleted. But, With has_and_belongs_to_many and has_many :through, the many to many scenario, the join records will be deleted, but the associated records won't.