4.12. Delete/Destroy a Record

To remove a database record, you can use the methods destroy and delete. It's quite easy to confuse these two terms, but they are different and after a while you get used to it.
As an example, we use the following Rails application:
$ rails new bookshelf
  [...]
$ cd bookshelf
$ rails generate model book title
  [...]
$ rails generate model author book_id:integer first_name last_name
  [...]
$ rake db:migrate
  [...]
$
app/models/book.rb
class Book < ActiveRecord::Base
  has_many :authors, dependent: :destroy
end
app/models/author.rb
class Author < ActiveRecord::Base
  belongs_to :book
end

destroy

With destroy you can remove a record and any existing dependencies are also taken into account (see for example :dependent => :destroy in the section called “Options”). Simply put: to be on the safe side, it's better to use destroy because then the Rails system does more for you.
Let's create a record and then destroy it again:
$ rails console
Loading development environment (Rails 4.0.0)
>> book = Book.create(title: 'Homo faber')
   (0.1ms)  begin transaction
  SQL (2.2ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 13:30:24 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 13:30:24 UTC +00:00]]
   (2.2ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 13:30:24", updated_at: "2013-07-16 13:30:24">
>> Book.count
   (0.3ms)  SELECT COUNT(*) FROM "books"
=> 1
>> book.destroy
   (0.2ms)  begin transaction
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 1]]
  SQL (0.3ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 1]]
   (0.6ms)  commit transaction
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 13:30:24", updated_at: "2013-07-16 13:30:24">
>> Book.count
   (0.4ms)  SELECT COUNT(*) FROM "books"
=> 0
>>
As we are using the option dependent: :destroy in the Book model, we can also automatically remove all authors:
>> Book.create(title: 'Homo faber').authors.create(first_name: 'Max', last_name: 'Frisch')
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 13:31:11 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 13:31:11 UTC +00:00]]
   (1.9ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 2], ["created_at", Tue, 16 Jul 2013 13:31:11 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 16 Jul 2013 13:31:11 UTC +00:00]]
   (1.0ms)  commit transaction
=> #<Author id: 1, book_id: 2, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 13:31:11", updated_at: "2013-07-16 13:31:11">
>> Author.count
   (0.4ms)  SELECT COUNT(*) FROM "authors"
=> 1
>> Book.first.destroy
  Book Load (0.3ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" ASC LIMIT 1
   (0.1ms)  begin transaction
  Author Load (0.1ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 2]]
  SQL (0.3ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 1]]
  SQL (0.1ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 2]]
   (2.1ms)  commit transaction
=> #<Book id: 2, title: "Homo faber", created_at: "2013-07-16 13:31:11", updated_at: "2013-07-16 13:31:11">
>> Author.count
   (0.4ms)  SELECT COUNT(*) FROM "authors"
=> 0
>>
When removing records, please always consider the difference between the content of the database table and the value of the currently removed object. The instance is frozen after removing the database field. So it is no longer in the database, but still present in the program, yet it can no longer be modified there. It is read-only. To check, you can use the method frozen?:
>> book = Book.create(title: 'Homo faber')
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 13:32:30 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 13:32:30 UTC +00:00]]
   (2.0ms)  commit transaction
=> #<Book id: 3, title: "Homo faber", created_at: "2013-07-16 13:32:30", updated_at: "2013-07-16 13:32:30">
>> book.destroy
   (0.1ms)  begin transaction
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = ?  [["book_id", 3]]
  SQL (0.4ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 3]]
   (1.9ms)  commit transaction
=> #<Book id: 3, title: "Homo faber", created_at: "2013-07-16 13:32:30", updated_at: "2013-07-16 13:32:30">
>> Book.count
   (0.2ms)  SELECT COUNT(*) FROM "books"
=> 0
>> book
=> #<Book id: 3, title: "Homo faber", created_at: "2013-07-16 13:32:30", updated_at: "2013-07-16 13:32:30">
>> book.frozen?
=> true
>> 
The record has been removed from the database, but the object with all its data is still present in the running Ruby program. So could we then revive the entire record? The answer is yes, but it will then be a new record:
>> Book.create(title: book.title)
   (0.1ms)  begin transaction
  SQL (0.5ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 13:33:31 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 13:33:31 UTC +00:00]]
   (1.7ms)  commit transaction
=> #<Book id: 4, title: "Homo faber", created_at: "2013-07-16 13:33:31", updated_at: "2013-07-16 13:33:31">
>> exit
$

delete

With delete you can remove a record directly from the database. Any dependencies to other records in the model are not taken into account. The method delete only deletes that one row in the database and nothing else.
Let's create a book with one author and then remove the book with delete:
$ rake db:reset
  [...]
$ rails console
Loading development environment (Rails 4.0.0)
>> Book.create(title: 'Homo faber').authors.create(first_name: 'Max', last_name: 'Frisch')
   (0.1ms)  begin transaction
  SQL (2.2ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 16 Jul 2013 13:35:49 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 16 Jul 2013 13:35:49 UTC +00:00]]
   (2.5ms)  commit transaction
   (0.0ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 1], ["created_at", Tue, 16 Jul 2013 13:35:49 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 16 Jul 2013 13:35:49 UTC +00:00]]
   (0.9ms)  commit transaction
=> #<Author id: 1, book_id: 1, first_name: "Max", last_name: "Frisch", created_at: "2013-07-16 13:35:49", updated_at: "2013-07-16 13:35:49">
>> Author.count
   (0.3ms)  SELECT COUNT(*) FROM "authors"
=> 1
>> Book.last.delete
  Book Load (0.2ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
  SQL (2.9ms)  DELETE FROM "books" WHERE "books"."id" = 1
=> #<Book id: 1, title: "Homo faber", created_at: "2013-07-16 13:35:49", updated_at: "2013-07-16 13:35:49">
>> Author.count
   (0.4ms)  SELECT COUNT(*) FROM "authors"
=> 1
>> Book.count
   (0.4ms)  SELECT COUNT(*) FROM "books"
=> 0
>> exit
$
The record of the book 'Homo faber' is deleted, but the author is still in the database.
As with destroy, an object also gets frozen when you use delete (see the section called “destroy”). The record is already removed from the database, but the object itself is still there.