10.9 Migration-Befehle im Detail 

Wir haben bereits die beiden Befehle add_column und remove_column kennengelernt, mit denen das Datenbankschema verändert werden kann. In diesem Abschnitt werden wir alle Befehle im Detail vorstellen.
Datentypen 

kap_migraiton_datentypenJedes Feld in einer Datenbanktabelle gehört einem bestimmten SQL-Datentyp an.
Die verschiedenen Datenbanksysteme haben zwar viele SQL-Datentypen gemeinsam, wie z. B. INT, CHAR, VARCHAR, jedoch gibt es auch Datentypen, die nicht in jedem Datenbanksystem verfügbar sind. Ein Beispiel hierfür ist der Datentyp BOOLEAN, mit dem Wahr/Falsch- bzw. True/False-Werte gespeichert werden können. Im Datenbanksystem PostgreSQL ist der Datentyp verfügbar, in MySQL jedoch nicht.
Ab MySQL Version 4.1 kann der Datentyp BOOLEAN verwendet werden. Dies ist jedoch nur ein Alias für den Datentyp TINYINT(1), d. h., wenn Sie ein Feld mit dem Typ BOOLEAN anlegen, wird in Wirklichkeit TINYINT(1) verwendet, in dem eine einstellige Zahl gespeichert werden kann. Die Zahl 1 entspricht dann dem Wert true, und 0 dem Wert false . In Zukunft soll ein »richtiger« BOOLEAN-Datentyp in MySQL integriert werden, um damit dem SQL-Standard gerecht zu werden.
Dank Rails brauchen wir uns aber um diese Details nicht zu kümmern.
Migration-Datentyp | Erläuterung |
:string | Zeichenkette mit maximal 255 Zeichen |
:text | Zeichenkette mit mehr als 255 Zeichen |
:integer | Ganzzahlige Werte, sowohl positive als auch negative Zahlen |
:float | Fließkommazahlen |
:decimal | Dezimalzahlen mit fester Anzahl an Nachkommastellen. Verwenden Sie diesen Typ z. B. für Währungen, da es nicht wie beim Typ :float zu Rundungsfehlern kommen kann. Sie sollten die Optionen precision (Anzahl Gesamtstellen) und scale (Anzahl Nachkommastellen) verwenden. |
:datetime | Datum und Zeit |
:timestamp | Zeitstempel. Speichert aktuelle Zeit und aktuelles Datum, wenn ein Datensatz hinzugefügt oder geändert wurde. |
:time | Nur die Zeit |
:date | Nur das Datum |
:binary | Binärdaten, wie z. B. Bilder, mp3-Dateien |
:boolean | Wahrheitswert true oder false |
Tabelle Datentypen
Erstellungsdatum
Wenn Sie ein Feld vom Typ :date oder :datetime mit dem Namen created_at oder created_on erstellen, setzt Rails automatisch das Erstellungsdatum (und die -zeit) in diesem Feld.
Änderungsdatum
Sehr häufig möchte man auch wissen, wann eine Zeile bzw. ein Datensatz geändert wurde. Legen Sie dazu ein Feld updated_at oder updated_on vom Typ :date an.
Beachten Sie, dass diese Automatik nicht vom Datenbanksystem, sondern von Rails ausgeführt wird. Dieser Hinweis ist insbesondere dann von Bedeutung, wenn Sie nicht ausschließlich von Rails aus auf die Datenbank zugreifen, sondern z. B. auch über PHP.
Tabellenfelder verwalten 

Felder hinzufügen
Um ein neues Feld zu einer Tabelle hinzuzufügen, verwenden Sie den folgenden Befehl:
add_column Tabellenname, Feldname, Datentyp, Optionen
Felder werden in Rails mit Kleinbuchstaben benannt. Sollte es sich um zusammengesetzte Begriffe handeln, werden die einzelnen Begriffe durch Unterstriche miteinander verbunden:
Listing Hinzufügen von Tabellenfeldern
add_column :products, :price, :decimal, precision => 5, :scale => 2 add_column :products, :active, :boolean add_column :products, :name, :string, :limit => 100 add_column :products, :stock, :integer, default=>0, :null => false add_column :products, :picture, :binary, :limit=> 2.megabytes add_column :products, :booking_date, :datetime
Im obigen Beispiel kann das Feld price Zahlen von -999.99 bis +999.99 speichern.
Felder löschen
Ein Tabellenfeld wird einfach mit folgendem Befehl gelöscht:
remove_column Tabellenname, Feldname
In diesem Fall ist ein Wiederherstellen der Daten in dem betreffenden Feld nicht mehr möglich, auch wenn Sie in der Methode self.down den Befehl add_column zur Wiederherstellung des Feldes verwenden.
Felder umbenennen
Tabellenfeldnamen sollten unbedingt für sich sprechen, wie z. B. der Feldname info1 . Um Felder umzubenennen, können Sie den Befehl
rename_column Tabellenname , Feldname , NeuerFeldname
verwenden.
Datentyp von Feldern ändern
Sie können nicht nur ein Feld umbenennen, sondern auch nachträglich den Datentyp des Feldes ändern. Verwenden Sie dazu den Befehl:
change_column Tabellenname , Feldname , Datentyp , Optionen
Wenn Sie den Datentyp eines Feldes ändern, kann es zu Datenverlust kommen.
Listing Beispiele für die Verwendung von change_column
change_column :products, :description, :text change_column :products, :name, :string, :limit=>120
Tabellen verwalten 

Tabellen erstellen
Sie können nicht nur Felder zu bereits bestehenden Tabellen hinzufügen, sondern auch neue Tabellen erstellen. Der Befehl
create_table(Tabellenname,Optionen) do |t| t.datentyp Feldname, Optionen ... t.timestamps end
Tabellennamen im Plural
erstellt eine Tabelle mit dem angegebenen Tabellennamen und den angegebenen Feldern. Rails geht davon aus, dass Tabellennamen im Plural angegeben werden. Verwenden Sie am besten auch nur englische Namen, damit Rails automatisch die Singularform des Namens bilden kann.
Der create -Befehl erstellt automatisch einen Primärschlüssel mit dem Namen id und dem Datentyp integer . Der Primärschlüssel ist immer dann notwendig, wenn eine Model-Klasse auf die Tabelle zugreift. In seltenen Fällen ist es sinnvoll, mit der Option :id => false die automatische Generierung des Primärschlüssels zu deaktivieren (siehe Abschnitt 10.10.3).
Im nächsten Abschnitt finden Sie ein vollständiges Beispiel.
t.timestamps
Mit dem Ausdruck t.timestamps werden automatisch die Felder updated_at und created_at vom Typ datetime erstellt, die bei Aktualisierung (update) oder Hinzufügen eines Datensatzes automatisch von Rails auf das aktuelle Datum gesetzt werden.
Tabellen löschen
Wenn wir eine Tabelle erstellen können, müssen wir auch eine Tabelle löschen können. Der Befehl
drop_table Tabellenname
löscht die gesamte Tabelle mit allen Daten.
Im folgenden Beispiel wird in der Methode self.up eine Tabelle hinzugefügt und in der Methode self.down wieder entfernt.
Das Migration-Skript wurde zunächst mit dem Model-Generator erstellt.
ruby script/generate model product name:integer \ description:string price:decimal, visible:boolean
Ergänzung
Da bei der Generierung der Migration-Datei nicht die Optionen angegeben werden können, ergänzen wir diese entsprechend:
class CreateProducts < ActiveRecord::Migration def self.up create_table :products do |t| # Zeichenkette auf 100 Zeichen beschränken t.string :name, :limit => 100 t.string :description # Typ decimal mit den 8 Stellen insgesamt # und 2 Nachkommastellen t.decimal :price, precision => 8, :scale => 2 # Standardwert vom Feld visible ist true # und das Feld darf nicht leer sein. t.boolean :visible, :null=>false, default=>true # Erstellt die Felder created_at und updated_at t.timestamps end end def self.down drop_table :products end end
Tabellen umbenennen
rename_table
Eine Tabelle kann mit dem Befehl
rename_table Tabellenname, NeuerTabellenName
umbenannt werden.
Indizes verwalten 

Ein Datenbanksystem kann auf Daten nur effizient zugreifen, wenn die Felder, nach denen gesucht wird, indiziert werden. Um einen Index über Rails anzulegen, wird der Befehl
add_index Tabellenname, Feldname, Optionen
angewendet.
Index bennen
Mit der Option :name=>"mein index" kann der Name des Index angegeben werden. Wird der Name nicht angegeben, so setzt Rails den Namen, indem es den Namen der Tabelle und den Namen des Feldes mit einem Unterstrich verbindet. Es ist daher normalerweise nicht nötig, einen Namen anzugeben.
Eindeutiger Index
Die Option :unique=>true legt einen eindeutigen Index fest, der verhindert, dass in einem Tabellenfeld doppelte Werte vorkommen.
Es ist sogar möglich, einen Index über mehrere Felder anzulegen, indem die Feldnamen als Array angegeben werden.
Die folgenden Beispiele zeigen die Verwendung des Befehls add_index:
Listing Hinzufügen von Indizes
add_index :products, :price add_index :products, :name, :unique=>true add_index :persons, [:vorname, :nachname], :name=>'person_vorname_nachname'
Löschen von Indizes
Zum Löschen von Indizes gibt es mehrere Varianten, abhängig davon, ob Sie den Namen des Index angegeben haben oder nicht. Wenn Sie dem Index keinen expliziten Namen gegeben haben, so verwenden Sie den Befehl:
remove _index , :column=> Feldname
Wenn Sie hingegen den Index selbst mit :name=>'..' benannt haben, so verwenden Sie diese Variante:
remove _index , :name=> Index Name
Beispieldaten hinzufügen 

Daten hinzufügen, ändern, löschen
Sie können mit Migrations nicht nur das Datenbankschema verändern, sondern sogar Daten hinzufügen, ändern oder löschen. Dies ist z. B. praktisch, um Beispieldatensätze hinzuzufügen. Ihnen stehen dazu die Befehle der Klasse ActiveRecord zur Verfügung.
Im folgenden Beispiel wird eine Produkt-Tabelle erstellt und zwei Datensätze mit der Klassen-Methode create hinzugefügt. Voraussetzung ist, dass die Model-Klasse bereits vorhanden ist. Dies ist z. B. gegeben, wenn Sie die Migration-Datei mit dem Model- oder Scaffold-Generator erstellen:
class CreateProducts < ActiveRecord::Migration def self.up create_table :products do |t| t.string :name t.decimal :price, :precision => 8, :scale => 2 t.boolean :enabled, :default => true t.timestamps end Product.create (:name => "iPod nano 3G", :price => 149.00) Product.create (:name => "Mac Book", :price => 1100.00) end def self.down drop_table :products end end
SQL-Befehle direkt verwenden 

Sie stoßen mit den Migration-Befehlen zuweilen an Grenzen. In diesem Fall bietet Rails die Möglichkeit, auch SQL in die Migration-Skripte einzufügen. Dies ist jedoch dann meist nicht mehr datenbankunabhängig.
execute 'SQL-Befehle'
Schnappschuss eines Datenbankschemas 

rake db:schema:dump
Bei jeder Ausführung des Rake-Tasks rake db:migrate werden nicht nur die Migration-Skripte ausgeführt, sondern automatisch auch der Rake-Task rake db:schema:dump.
Der Rake-Task rake db:schema:dump analysiert den aktuellen Stand der Datenbank und erstellt daraus eine Ruby-Datei db/schema.rb mit allen Tabellendefinitionen und den zugehörigen Tabellen. Es handelt sich also um einen »Schnappschuss« des aktuellen Datenbankschemas.
In der Schema-Datei schema.rb sind create_table - und gegebenenfalls create_index -Befehle enthalten, die wir bereits kennen. Der entscheidende Unterschied liegt darin, dass im Schema-Skript eine Versionsnummer in der ersten Zeile steht. Diese Versionsnummer gibt an, welches Migration-Skript als Letztes ausgeführt wurde.
Listing Beispiel einer Schema-Datei db/schema.rb
ActiveRecord::Schema.define(:version => 6) do create_table "bonus_cards", :force => true do |t| t.integer "points" t.integer "passenger_id" t.datetime "created_at" t.datetime "updated_at" end create_table "countries", :force => true do |t| t.string "name" t.string "code" t.datetime "created_at" t.datetime "updated_at" end ...
Die Schema-Datei ist z. B. sehr praktisch, um nachzusehen, welche Felder eine Tabelle besitzt.
Wenn Sie im Verlauf Ihrer Entwicklung viele Migration-Skripte angelegt haben und Sie die Migration-Skripte mit rake db:migrate auf einer neuen Datenbank ausführen, werden alle Migration-Skripte ausgeführt, bis die Datenbank den aktuellen Stand hat. Dies kann bei z. B. 10 Migrationen viel Zeit in Anspruch nehmen. Auch für dieses Problem bietet Rails eine Lösung:
rake db:schema:load
Anstatt mit rake db:migrate alle Migration-Skripte auszuführen, rufen Sie einfach den Rake-Task
rake db:schema:load
auf. Dieses Kommando führt die Schema-Datei db/schema.rb aus, indem es die Tabellen und Indizes erstellt. Außerdem wird die Tabelle schema_info in der Datenbank erstellt und die Versionsnummer, die dem Schema-Skript entnommen wird, eingetragen (z. B. »6«). Damit wird verhindert, dass bei Ausführung des Kommandos rake db:migrate ältere Migration-Skripte noch einmal ausgeführt werden.
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.