Neu: Das englische Ruby on Rails 4.0 Buch.

4.12. Einen Datensatz löschen

Es gibt die Methoden destroy und delete, um Datensätze zu löschen. Diese Wortwahl und die damit einhergehenden Unterschiede empfinde ich für deutschsprachige Anfänger als schwierig. Aber mit der Zeit gewöhnt man sich dran.
Als Beispiel benutzen wir folgende Rails-Applikation:
MacBook:~ xyz$ rails new bookshelf
[...]
MacBook:~ xyz$ cd bookshelf 
MacBook:bookshelf xyz$ rails generate model book title
[...]
MacBook:bookshelf xyz$ rails generate model author book_id:integer first_name last_name
[...]
MacBook:bookshelf xyz$ rake db:migrate
[...]
MacBook:bookshelf xyz$
app/models/book.rb
class Book < ActiveRecord::Base
  attr_accessible :title

  has_many :authors, :dependent => :destroy
end
app/models/author.rb
class Author < ActiveRecord::Base
  attr_accessible :book_id, :first_name, :last_name

  belongs_to :book
end

destroy

Mit destroy können Sie einen Datensatz löschen und dabei werden auch vorhandene Abhängigkeiten beachtet (siehe z. B. :dependent => :destroy in „Optionen“). Vereinfacht kann man sagen: Lieber destroy nehmen; das ist sicherer, weil das Rails-System dann mehr mitdenkt.
Legen wir einmal einen Datensatz an und löschen ihn danach wieder:
MacBook:bookshelf xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > book = Book.create(:title => 'Homo faber')
   (0.1ms)  begin transaction
  SQL (6.2ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 08 May 2012 08:10:58 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 08 May 2012 08:10:58 UTC +00:00]]
   (1.3ms)  commit transaction
 => #<Book id: 1, title: "Homo faber", created_at: "2012-05-08 08:10:58", updated_at: "2012-05-08 08:10:58"> 
1.9.3p194 :002 > Book.all
  Book Load (0.3ms)  SELECT "books".* FROM "books" 
 => [#<Book id: 1, title: "Homo faber", created_at: "2012-05-08 08:10:58", updated_at: "2012-05-08 08:10:58">] 
1.9.3p194 :003 > book.destroy
   (0.1ms)  begin transaction
  Author Load (0.1ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 1
  SQL (0.3ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 1]]
   (1.1ms)  commit transaction
 => #<Book id: 1, title: "Homo faber", created_at: "2012-05-08 08:10:58", updated_at: "2012-05-08 08:10:58"> 
1.9.3p194 :004 > Book.all
  Book Load (0.3ms)  SELECT "books".* FROM "books" 
 => [] 
1.9.3p194 :005 >
Da wir im Book Model die Option :dependent => :destroy benutzen, können wir beim Löschen auch automatisch alle Autoren mitlöschen:
1.9.3p194 :005 > 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, 08 May 2012 08:46:14 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 08 May 2012 08:46:14 UTC +00:00]]
   (3.7ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 2], ["created_at", Tue, 08 May 2012 08:46:14 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 08 May 2012 08:46:14 UTC +00:00]]
   (0.8ms)  commit transaction
 => #<Author id: 1, book_id: 2, first_name: "Max", last_name: "Frisch", created_at: "2012-05-08 08:46:14", updated_at: "2012-05-08 08:46:14"> 
1.9.3p194 :006 > Book.all
  Book Load (0.3ms)  SELECT "books".* FROM "books" 
 => [#<Book id: 2, title: "Homo faber", created_at: "2012-05-08 08:46:14", updated_at: "2012-05-08 08:46:14">] 
1.9.3p194 :007 > Author.all
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" 
 => [#<Author id: 1, book_id: 2, first_name: "Max", last_name: "Frisch", created_at: "2012-05-08 08:46:14", updated_at: "2012-05-08 08:46:14">] 
1.9.3p194 :008 > Book.first.destroy
  Book Load (0.3ms)  SELECT "books".* FROM "books" LIMIT 1
   (0.1ms)  begin transaction
  Author Load (0.2ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 2
  SQL (0.4ms)  DELETE FROM "authors" WHERE "authors"."id" = ?  [["id", 1]]
  SQL (0.1ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 2]]
   (2.4ms)  commit transaction
 => #<Book id: 2, title: "Homo faber", created_at: "2012-05-08 08:46:14", updated_at: "2012-05-08 08:46:14"> 
1.9.3p194 :009 > Author.all
  Author Load (0.3ms)  SELECT "authors".* FROM "authors" 
 => [] 
1.9.3p194 :010 >
Bitte denken Sie beim Löschen immer an den Unterschied des Inhaltes der Datenbank und des Wertes des aktuell gelöschten Objektes. Die Instanz wird nach dem Löschen des Datenbank-Feldes frozen (eingefroren). Sie ist also in der Datenbank nicht mehr, aber im Programm noch da, kann aber dort nicht verändert werden. Lesen geht aber. Zum Überprüfen gibt es die Methode frozen?:
1.9.3p194 :010 > book = Book.create(:title => 'Homo faber')
   (0.1ms)  begin transaction
  SQL (2.6ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 08 May 2012 08:15:01 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 08 May 2012 08:15:01 UTC +00:00]]
   (3.5ms)  commit transaction
 => #<Book id: 3, title: "Homo faber", created_at: "2012-05-08 08:15:01", updated_at: "2012-05-08 08:15:01"> 
1.9.3p194 :011 > book.destroy
   (0.1ms)  begin transaction
  Author Load (0.3ms)  SELECT "authors".* FROM "authors" WHERE "authors"."book_id" = 3
  SQL (0.4ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 3]]
   (2.5ms)  commit transaction
 => #<Book id: 3, title: "Homo faber", created_at: "2012-05-08 08:15:01", updated_at: "2012-05-08 08:15:01"> 
1.9.3p194 :012 > Book.all
  Book Load (0.2ms)  SELECT "books".* FROM "books" 
 => [#<Book id: 2, title: "Homo faber", created_at: "2012-05-08 08:13:09", updated_at: "2012-05-08 08:13:09">] 
1.9.3p194 :013 > book
 => #<Book id: 3, title: "Homo faber", created_at: "2012-05-08 08:15:01", updated_at: "2012-05-08 08:15:01"> 
1.9.3p194 :014 > book.frozen?
 => true 
1.9.3p194 :015 > 
Der Datensatz ist also in der Datenbank gelöscht, aber das Objekt mit allen Daten ist noch im laufenden Ruby-Programm vorhanden. Könnten wir damit den gesamten Datensatz wiederbeleben? Ja, allerdings ist das dann ein neuer Datensatz:
1.9.3p194 :018 > Book.create(:title => book.title)
   (0.1ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 08 May 2012 08:34:04 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 08 May 2012 08:34:04 UTC +00:00]]
   (3.4ms)  commit transaction
 => #<Book id: 4, title: "Homo faber", created_at: "2012-05-08 08:34:04", updated_at: "2012-05-08 08:34:04"> 
1.9.3p194 :019 > exit
MacBook:bookshelf xyz$ 

delete

Mit delete können Sie einen Datensatz direkt in der Datenbank löschen. Dabei werden keine Abhängigkeiten zu anderen Datensätzen im Model beachtet. Die Methode delete löscht also nur die eine Zeile in der Datenbank und sonst nichts.
Erstellen wir ein Buch mit einem Autor und löschen das Buch danach mit delete:
MacBook:bookshelf xyz$ rails console
Loading development environment (Rails 3.2.3)
1.9.3p194 :001 > Book.create(:title => 'Homo faber').authors.create(:first_name => 'Max', :last_name => 'Frisch')
   (0.1ms)  begin transaction
  SQL (0.7ms)  INSERT INTO "books" ("created_at", "title", "updated_at") VALUES (?, ?, ?)  [["created_at", Tue, 08 May 2012 08:41:08 UTC +00:00], ["title", "Homo faber"], ["updated_at", Tue, 08 May 2012 08:41:08 UTC +00:00]]
   (3.5ms)  commit transaction
   (0.1ms)  begin transaction
  SQL (0.4ms)  INSERT INTO "authors" ("book_id", "created_at", "first_name", "last_name", "updated_at") VALUES (?, ?, ?, ?, ?)  [["book_id", 6], ["created_at", Tue, 08 May 2012 08:41:08 UTC +00:00], ["first_name", "Max"], ["last_name", "Frisch"], ["updated_at", Tue, 08 May 2012 08:41:08 UTC +00:00]]
   (1.0ms)  commit transaction
 => #<Author id: 3, book_id: 6, first_name: "Max", last_name: "Frisch", created_at: "2012-05-08 08:41:08", updated_at: "2012-05-08 08:41:08"> 
1.9.3p194 :002 > Book.last.delete
  Book Load (0.3ms)  SELECT "books".* FROM "books" ORDER BY "books"."id" DESC LIMIT 1
  SQL (3.7ms)  DELETE FROM "books" WHERE "books"."id" = 6
 => #<Book id: 6, title: "Homo faber", created_at: "2012-05-08 08:41:08", updated_at: "2012-05-08 08:41:08"> 
1.9.3p194 :003 > Author.exists?(3)
  Author Exists (0.3ms)  SELECT 1 FROM "authors" WHERE "authors"."id" = 3 LIMIT 1
 => true 
1.9.3p194 :004 > ruby-1.9.2-p0 > exit
MacBook:bookshelf xyz$  
Es wird zwar der Datensatz des Buches 'Homo faber' gelöscht, aber der Autor ist noch in der Datenbank gespeichert.
Wie bei destroy, geht auch bei delete ein Objekt nach dem delete in den frozen-Status (siehe „destroy“). Der Datensatz ist also in der Datenbank bereits gelöscht, aber das Objekt an sich existiert noch.