5.4 Model 

Bis jetzt haben wir unsere Bookmarks manuell über ein Array im Controller gesetzt. Das ist aber nicht komfortabel für die Eingabe von neuen Bookmarks und auch nicht sinnvoll für die Verwaltung von vielen Bookmarks, schon gar nicht, wenn sie in strukturierter Form, wie zum Beispiel Angabe von URL und Titel, vorliegen.
Abbildung Model
Datenbank- konfigurationsdatei
Das heißt, wir möchten und sollten unsere Bookmarks in einer Datenbank verwalten. Bis jetzt haben wir aber noch keine Datenbank angelegt. In Rails wird die Verwaltung der Datenbank über eine Konfigurationsdatei database.yml im Verzeichnis config gesteuert:
Listing config/database.yml
development: adapter: sqlite3 database: db/development.sqlite3 timeout: 5000 test: adapter: sqlite3 database: db/test.sqlite3 timeout: 5000 production: adapter: sqlite3 database: db/production.sqlite3 timeout: 5000
Im Bereich development wird die Datenbank für die Entwicklungsumgebung konfiguriert. Die anderen beiden Umgebungen test und production ignorieren wir zunächst. Dazu später mehr.
Einstellungen
Sie können hier die Einstellungen für den verwendeten Datenbankadapter, den Namen und Pfad zur Datenbankdatei und den Timeout vornehmen. Die bereits vorhandenen Werte sind Standardwerte, die Rails beim Generieren des Projekts angelegt hat. Diese können Sie übernehmen oder nach Ihren Wünschen anpassen. Auf den Eintrag des verwendeten Datenbankadapters haben Sie beim Generieren eines Rails-Projektes Einfluss, indem Sie den Parameter --database Adaptername oder -d Adaptername übergeben:
rails projektname --database mysql
Standard: SQLite3
Lassen Sie wie wir diesen Parameter weg, setzt Rails den Wert standardmäßig auf sqlite3.
In unserem Beispiel übernehmen wir die von Rails gesetzten Standardwerte. Die SQLite3-Datenbankdatei development.sqlite3 hat Rails bei der Anlage des Projekts im Verzeichnis db angelegt.
Sollten Sie einen anderen Datenbankadapter verwenden, müssen Sie die Datenbank noch mit dem folgenden Befehl anlegen:
rake db:create
Zum Speichern unserer Bookmarks benötigen wir eine Tabelle, die die Werte zu unseren Bookmarks, wie die URL, den Titel und evtl. einen Kommentar, speichert. Da Rails objektorientiert ist und wir deshalb nicht über die manuelle Eingabe von SQL-Befehlen auf Tabellen zugreifen können, benötigen wir auch eine Klasse, über deren Objekte der Zugriff auf die Tabelle möglich ist.
Mit Models werden in Rails Klassen bezeichnet, die einen Zugriff auf Datenbanktabellen erlauben. Rails enthält dazu das Framework ActiveRecord .
Model-Generator
Das heißt, als Nächstes müssen wir ein Model erstellen. Dazu stellt uns Rails den model -Generator zur Verfügung, der als Pflichtparameter den Namen des Models im Singular erwartet:
ruby script/generate model bookmark
Der Generator legt folgende Verzeichnisse und Dateien an:
exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/bookmark.rb create test/unit/bookmark_test.rb create test/fixtures/bookmarks.yml create db/migrate create db/migrate/001_create_bookmarks.rb
Neben der Datei app/models/bookmark.rb für das Model hat der Generator auch eine Test-Datei test/unit/bookmark_test.rb, eine Datei test/fixtures/bookmarks.yml zum Anlegen von Testdaten (Fixtures), und eine Migration-Datei, db/migrate/001_create_bookmarks.rb, angelegt. Die Test-Datei und die Fixtures interessieren uns im Moment nicht. Wir wollen uns zunächst mit der Migration beschäftigen.
Migrations 

Migration ist ein ganz wichtiges Konzept in Rails. Innerhalb einer Migration-Datei wird die Tabellenstruktur einer Datenbanktabelle in Ruby-Code beschrieben. Auch Änderungen an einer Tabelle, weil ein Feld nicht mehr benötigt wird oder neue Felder hinzukommen, werden über Migration durchgeführt. Das heißt, in der Migration-Datei wird alles definiert: wie die Tabelle heißt, die erzeugt werden soll, welche Felder diese Tabelle hat, und welchen Datentyp diese Felder haben. Und das wird nicht etwa in SQL-Befehlen definiert, sondern in Ruby-Code, der speziell innerhalb des Frameworks ActiveRecord für die Lösung dieses Problems entwickelt wurde. Deshalb spricht man bei Migrations auch von einer Domain Specific Language . Die Migration-Datei kann ausgeführt werden - wie das geht, zeigen wir Ihnen gleich - und dann wird der darin enthaltene Ruby-Code in SQL-Befehle übersetzt. Eine ausführliche Beschreibung des Migration-Konzepts erhalten Sie in Kapitel 10.
Per Konvention ist definiert, dass zu einem Model eine Tabelle gehört, die so heißt wie das Model im Plural.
Migration-Datei
Das alles steckt in dem Generator, den wir zur Erzeugung des Models benutzt haben. Das heißt, zu der Model-Datei legt er auch eine Migration-Datei im Verzeichnis db/migrate an, die schon den Befehl zum Anlegen der Tabelle enthält:
Listing db/migrate/001_create_bookmarks.rb
class CreateBookmarks < ActiveRecord::Migration def self.up create_table :bookmarks do |t| t.timestamps end end def self.down drop_table :bookmarks end end
Felder ergänzen
Das, was wir noch ergänzen müssen, sind die einzelnen Feldnamen und Feldtypen. Wir benötigen die Felder title, url und comment . Der Eintrag t.timestamps erzeugt die beiden Datumsfelder created_at und updated_at, in denen gespeichert wird, wann ein Datensatz angelegt wurde und wann ein Datensatz geändert wurde. Rails setzt und aktualisiert diese beiden Felder später automatisch. Sehr praktisch.
Die Datentypen in der Migration haben eigene Bezeichnungen. Zum Beispiel heißt ein Varchar(255) in der Migration string:
Listing db/migrate/001_create_bookmarks.rb
class CreateBookmarks < ActiveRecord::Migration def self.up create_table :bookmarks do |t| t.string :title, :url t.text :comment t.timestamps end end def self.down drop_table :bookmarks end end
Sexy Migrations |
Hätten wir beim Generieren des Models auch die Feldnamen und die Feldtypen mit angegeben ( ruby script/generate model bookmark title:string url:string comment:text ), hätte der Generator die Migration-Datei inklusive der Feldnamen und -typen erstellt. |
rake db:migrate
Die Migration-Datei müssen wir jetzt ausführen, sprich in SQL-Befehle umwandeln. Wenn Sie diesen Umwandlungsprozess verfolgen wollen, öffnen Sie bitte zwei Konsolen-Fenster und wechseln in beiden in Ihr Projektverzeichnis. In dem Fenster, in dem Sie den Prozess verfolgen möchten, starten Sie den lokalen Rails-Server über ruby script/server, und in dem anderen Fenster führen Sie die Migration-Datei über folgenden Befehl aus:
rake db:migrate
Der Befehl liefert folgende Ausgabe:
== 1 CreateBookmarks: migrating =========================== -- create_table(:bookmarks) -> 0.0232s == 1 CreateBookmarks: migrated (0.0233s) ==================
Das sagt nur aus, dass die Migration-Datei fehlerfrei ausgeführt wurde. Den interessanteren Teil, was wirklich in SQL passiert ist, können Sie sich jetzt in dem Fenster ansehen, in dem Sie den Server gestartet hatten:
CREATE TABLE bookmarks ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "title" varchar(255) DEFAULT NULL, "url" varchar(255) DEFAULT NULL, "comment" text DEFAULT NULL, "created_at" datetime DEFAULT NULL, "updated_at" datetime DEFAULT NULL)
ID
Interessant ist, dass ein Feld id erstellt wurde, ohne dass wir dieses Feld in der Migration-Datei angegeben haben. Das Feld id wird immer automatisch als Primärschlüssel einer Tabelle miterstellt mit dem Attribut auto_increment, das heißt, jeder neue Datensatz erhält automatisch die nächsthöhere ID und ist deshalb nie leer.
Hätten wir nicht SQLite3, sondern zum Beispiel eine Oracle-Datenbank eingesetzt, wäre die Migration-Datei an Oracle angepasst übersetzt worden. Das heißt, die Migration-Datei ist immer gleich, egal welches Datenbanksystem verwendet wird.
ActiveRecord 

Der Zugriff auf die nun erstellte Tabelle bookmarks erfolgt über Objekte des Models Bookmark . Das heißt, das Model selbst repräsentiert die Tabelle, und die Objekte repräsentieren einen Datensatz in der Tabelle.
Wenn Sie das Model bookmark.rb im Verzeichnis app/models öffnen, sehen Sie, dass die Klasse Bookmark von ActiveRecord::Base erbt und ansonsten leer ist:
Listing app/models/bookmark.rb
class Bookmark < ActiveRecord::Base end
Das lassen wir auch erst einmal so, weil die Klasse alle Funktionalitäten, die zum Zugriff auf die Tabelle nötig sind, von ActiveRecord geerbt hat. Wie genau ActiveRecord funktioniert, erfahren Sie in Kapitel 10.
Datenbankzugriff in der Konsole testen 

ruby script/console
Sie können den Zugriff auf die Tabelle über den Aufruf der Rails-Konsole (ruby script/console) innerhalb der Rails-Umgebung testen. Die Rails-Konsole erlaubt es Ihnen, die Befehle interaktiv auszuprobieren. In dieser Konsole stehen Ihnen alle Klassen Ihres Projekts zur Verfügung. Das heißt, Sie können zum Beispiel alle bisher angelegten Bookmark-Datensätze zählen:
ruby script/console
Loading development environment (Rails 2.0.2)
>> Bookmark.count
=> 0
Magie von ActiveRecord
Um einen neuen Eintrag in der Tabelle bookmarks vorzunehmen, erzeugen Sie ein neues Objekt der Klasse Bookmark und weisen diesem Objekt die einzelnen Feldwerte über Attribute zu. Das ist die Magie, die ActiveRecord uns zur Verfügung stellt. ActiveRecord erzeugt nämlich Methoden, die so heißen wie die einzelnen Felder der Tabelle, um die Feldwerte auszulesen. Um einen Wert zu setzen, erhält die Methode ein Gleichheitszeichen am Ende. Und da die Klasse Bookmark von ActiveRecord erbt, steht diese Funktionalität auch in der Klasse Bookmark zur Verfügung:
>> bookmark = Bookmark.new => #<Bookmark id: nil, title: nil, url: nil, ... >> bookmark.title = "Ruby on Rails" => "Ruby on Rails" >> bookmark.url = "http://www.rubyonrails.com" => "http://www.rubyonrails.com"
Speichern können Sie den neuen Eintrag über den Aufruf von:
>> bookmark.save
Wenn Sie den lokalen Rails-Server noch gestartet hatten, konnten Sie nach dem Aufruf des save -Befehls Folgendes beobachten:
INSERT INTO bookmarks ("updated_at", "title", "url", "comment", "created_at") VALUES('2007-12-20 21:12:15', 'Ruby on Rails', 'http://www.rubyonrails.com', NULL, '2007-12-20 21:12:15')
Das heißt, es ist wirklich ein Datensatz hinzugefügt worden:
>> Bookmark.count => 1
create
Eine kürzere Möglichkeit, einen neuen Datensatz anzulegen, ist die Anwendung der Methode create, der Sie die einzelnen Werte als Hash übergeben:
>> Bookmark.create(:title => "Ruby", :url => "http://www.ruby-lang.org") => #<Bookmark id: 2, title: "Ruby", url: "http://www.ruby-lang.org", created_at: "2007-12-01 18:56:45", ... >> Bookmark.count => 2
find
Über die Methode find können Sie auf die einzelnen Datensätze zugreifen. Dazu übergeben Sie der Methode die ID des gesuchten Datensatzes als Parameter:
>> b = Bookmark.find(1) => #<Bookmark id: 1, title: "Ruby on Rails", url: "http://www.rubyonrails.com", created_at: "2007-12-01 18:52:02", ...
Auf die einzelnen Werte greifen Sie durch Aufruf der Methoden für die Feldnamen zu:
>> b.title => "Ruby on Rails" >> b.url => "http://www.rubyonrails.com"
Ausgabe eines Objektes |
Wenn Sie alle Werte eines Objektes ausgeben möchten, können Sie den
y -Befehl in der Rails-Konsole verwenden:
>> y Bookmark.find(1) --- !ruby/object:Bookmark attributes: updated_at: 2007-12-04 22:47:13 title: Ruby on Rails url: http://www.rubyonrails.com |
find(:all)
Um alle angelegten Bookmarks auszulesen, übergeben Sie der Methode find den Parameter :all . Das Ergebnis ist ein Array:
>> Bookmark.find(:all) => [#<Bookmark id: 1, title: "Ruby on Rails", url: "http://www.rubyonrails.com", created_at: "2007-12-01 18:52:02", ..., #<Bookmark id: 2, title: "Ruby", url: "http://www.ruby-lang.org", created_at: "2007-12-01 18:56:45" ...]
Wenn Sie zum Beispiel nur den Titel aller vorhandenen Bookmarks ausgeben möchten, können Sie das lösen, indem Sie mit einer each -Schleife über das Ergebnisarray von find(:all) iterieren und immer nur den Titel jedes Elementes ausgeben:
>> Bookmark.find(:all).each do |bookmark| ?> puts bookmark.title >> end Ruby on Rails Ruby
Oder Sie erzeugen ein Array, das nur die Titel der einzelnen Datensätze enthält:
>> Bookmark.find(:all).collect(&:title) => ["Ruby on Rails", "Ruby"]
Aber zunächst noch weitere Informationen zum Umgang mit ActiveRecord-Klassen. Zur Zeit ist es möglich, einen leeren Datensatz anzulegen, indem wir die Methode create aufrufen, ohne ihr Werte zu übergeben:
>> Bookmark.create => #<Bookmark id: 3, title: nil, url: nil, ... >> Bookmark.count => 3
Pflichtfelder definieren
Da wir nicht wollen, dass leere Datensätze angelegt werden, können wir im Model angeben, welche Felder Pflichtfelder sind. Das heißt, wenn diese Felder dann nicht gesetzt sind, wird der Datensatz nicht gespeichert. In unserem Beispiel soll der Datensatz nur gespeichert werden, wenn die Felder title und url gesetzt sind:
Listing app/models/bookmark.rb
class Bookmark < ActiveRecord::Base validates_presence_of :title validates_presence_of :url end
reload!
Da wir eine Änderung am Model vorgenommen haben, müssen wir, bevor wir testen, ob die Änderungen umgesetzt werden, die Applikation neu starten, da die Konsole beim Start die Entwicklungsumgebung lädt und unsere Änderung nach dem Laden erfolgt ist. Die Applikation wird über den Befehl reload! neu gestartet:
>> reload! Reloading... => true
Jetzt ist es nicht mehr möglich, einen leeren Datensatz zu speichern. Das können wir leicht kontrollieren, indem wir zuerst die aktuelle Anzahl der bereits angelegten Datensätze mit dem Befehl Bookmark.count abfragen, anschließend versuchen, einen leeren Datensatz über den Befehl Bookmark.create anzulegen, und dann noch einmal die Anzahl der angelegten Datensätze kontrollieren:
>> Bookmark.count => 3 >> Bookmark.create => #<Bookmark id: nil, title: nil, url: nil, created_at: nil, ... >> Bookmark.count => 3
Wenn dagegen die beiden Pflichtfelder gesetzt sind, wird ein neuer Datensatz angelegt:
>> Bookmark.count => 3 >> Bookmark.create(:title => "css Zen Garden", :url => "http://www.csszengarden.com") => #<Bookmark id: 4, title: "css Zen Garden", url: "http://www.csszengarden.com", created_at: "2007-12-01 19:21:58" ... >> Bookmark.count => 4
destroy
Wenn wir uns jetzt wieder die Titel aller bisher angelegten Bookmarks ausgeben lassen, befindet sich der leere Datensatz mitten in dem Array. Sie können diesen Datensatz mit Hilfe der Methode find_by_title, der Sie den Wert nil übergeben, suchen und mit der Methode destroy löschen:
>> Bookmark.find(:all).collect(&:title)
=> ["Ruby on Rails", "Ruby", nil, "css Zen Garden"]
>>
Bookmark.find_by_title(nil).destroy
...
>> Bookmark.find(:all).collect(&:title)
=> ["Ruby on Rails", "Ruby", "css Zen Garden"]
Wir haben jetzt in der ruby script/console so mit dem Model gearbeitet, wie wir das später im Controller tun werden. Dazu aber im nächsten Abschnitt mehr.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.