10.8 Migration-Skripte 

Der folgende Überblick zeigt Ihnen, wie ein typischer Arbeitsablauf mit Migration durchgeführt wird.
- Migration-Skript generieren mit einem Migration-, Model- oder Scaffold-Generator
- Befehle einfügen, die die Änderung am Datenbankschema beschreiben
- Migration-Skript ausführen mit rake db:migrate
Es ist auch möglich, mehrere Migration-Skripte zu erstellen und sie dann in einem Schritt gemeinsam auszuführen.
Migration-Skripte generieren 

Generator
Zur Erstellung der Migration-Skripte stellt Rails einige sehr hilfreiche Generatoren zur Verfügung. Generatoren haben Sie bereits mehrfach für die Generierung von Scaffolds, Models und Controllern kennengelernt.
Je nach Anwendungszweck können Sie einen der folgenden Generatoren für die Erstellung der Migration-Skripte verwenden:
- Migration-Generator
Der Migration-Generator erstellt nur ein Migration-Skript. Dies ist sinnvoll, um z. B. Felder einer Tabelle zu ändern, hinzuzufügen oder zu entfernen. Zum Erstellen einer neuen Tabelle sollten Sie besser den Model-Generator oder den Scaffold-Generator verwenden. Der Migration-Generator kann auf drei verschiedene Weisen verwendet werden. Um eine leeres Migration-Skript zu erstellen, das nur das »Gerüst« einer Migration-Datei enthält, gehen Sie wie folgt vor:ruby script/generate migration NameDesMigrations ruby script/generate migration ChangeCodeInCountry exists db/migrate create db/migrate/001_change_code_in_country.rb
In der generierten Migration-Datei können dann die gewünschten Migration-Befehle hinzugefügt werden.
class ChangeCodeInCountry < ActiveRecord::Migration def self.up end def self.down end end
Feld hinzufügen
Um ein Feld zu einer Tabelle hinzuzufügen, setzen Sie den Generator wie folgt ein:
ruby script/generate migration Add Felder To Tabellenname.
Optional können Sie Tabellenfeldname:Datentyp -Paare hinzufügen. In diesem Fall werden die Befehle zum Anlegen der neuen Felder automatisch in das Migration-Skript eingefügt.
Im folgenden Beispiel wird das Feld fax zur Tabelle passengers hinzugefügt:
ruby script/generate migration AddFaxToPassengers fax:string exists db/migrate create db/migrate/004_add_fax_to_passenger.rb
Die generierte Datei enthält bereits die Migration-Befehle zum Hinzufügen des Feldes:
class AddFaxToPassengers < ActiveRecord::Migration def self.up add_column :passengers, :fax, :string end def self.down remove_column :passengers, :fax end end
Felder löschen
Zum Löschen von Feldern kann die Form
ruby script/generate migration Remove Felder From Tabellenname
verwendet werden. Optional können Sie auch hier Tabellenfeldname:Datentyp-Paare übergeben.
Im folgenden Beispiel wird das Feld memo aus der Tabelle flights entfernt:
ruby script/generate migration RemoveMemoFromFlights \ memo:text exists db/migrate create db/migrate/005_remove_memo_from_flights.rb
Die Migration-Datei enthält ensprechend die passenden Einträge zum Löschen des Feldes:
class RemoveMemoFromFlights < ActiveRecord::Migration def self.up remove_column :flights, :memo end def self.down add_column :flights, :memo, :text end end
- Model-Generator
Der Model-Generator generiert ein Migration-Skript zur Erstellung einer Tabelle mit den angegebenen Feldern und eine Model-Klasse mit dem angegebenen Namen. Außerdem werden Test-Skripte und Test-Daten (sogenannte Fixtures) generiert.
ruby script/generate model BonusCard points:integer passenger_id:integer ... create app/models/bonus_card.rb create test/unit/bonus_card_test.rb create test/fixtures/bonus_cards.yml exists db/migrate create db/migrate/002_create_bonus_cards.rb
Die Migration-Datei enthält bereits die Befehle zum Erstellen der Tabelle mit den entsprechenden Feldern. Sie können selbst noch Ergänzungen hinzufügen.
class CreateBonusCards < ActiveRecord::Migration def self.up create_table :bonus_cards do |t| t.integer :points t.integer :passenger_id t.timestamps end end def self.down drop_table :bonus_cards end end
- Scaffold-Generator
Der scaffold -Generator macht das Gleiche wie der Model-Generator. Zusätzlich wird der Controller mit sämtlichen zugehörigen Views zur Verwaltung der Datensätze generiert:
ruby script/generate scaffold Country name:string \ code:string ... create app/views/countries create app/views/countries/index.html.erb create app/views/countries/show.html.erb create app/views/countries/new.html.erb create app/views/countries/edit.html.erb create app/views/layouts/countries.html.erb create public/stylesheets/scaffold.css create app/models/country.rb create test/unit/country_test.rb create test/fixtures/countries.yml create db/migrate create db/migrate/003_create_countries.rb create app/controllers/countries_controller.rb create test/functional/countries_controller_test.rb create app/helpers/countries_helper.rb route map.resources :countries
Es wird u. a. das Migration-Skript nnn_create_countries.rb erzeugt. Dieses Migration-Skript enthält die Befehle zum Erstellen der Tabelle mit den angegebenen Feldern.
class CreateCountries < ActiveRecord::Migration def self.up create_table :countries do |t| t.string :name t.string :code t.timestamps end end def self.down drop_table :countries end end
Namenskonvention
Der Name des generierten Skripts setzt sich aus einer dreistelligen laufenden Nummer und dem angegebenen Migrationnamen zusammen. Die Nummerierung beginnt bei 001 und wird bei erneutem Aufruf des migration -Generators jeweils um 1 erhöht. Wenn wir z. B. als Migrationnamen RemoveMemoFromFlights verwenden und bereits vorher ein Migration-Skript erstellt haben, erhalten wir 002_remove_memo_from_flights.rb.
Mit der Zeit werden Sie mehrere Migration-Skripte erstellen, die sich alle im Verzeichnis db/migrate befinden. Das Verzeichnis könnte z. B. folgende Migration-Skripte enthalten:
Listing Inhalt des Verzeichnisses db/migrate
001_create_countries.rb 002_create_airports.rb 003_create_passengers.rb 004_add_fax_to_passengers.rb
Wenn Sie die Bezeichnung der Migration-Skripte gut gewählt haben, können Sie leicht nachvollziehen, welche Änderungen an Ihrem Datenbankschema mit den Skripten vollzogen werden.
self.up, self.down
Lassen Sie uns nun einen Blick auf ein generiertes Migration-Skript werfen. Ein Migration-Skript enthält immer zwei Methoden: self.up und self.down.
Listing db/migration/005_add_fax_to_passengers.rb
class AddFaxToPassengers < ActiveRecord::Migration def self.up add_column :passengers, :fax, :string end def self.down remove_column :passengers, :fax end end
Die Methode self.up
Die Befehle, welche die Änderung des Datenbankschemas beschreiben, werden innerhalb der Methode self.up angegeben. Um z. B. ein Tabellenfeld hinzuzufügen, steht die Methode add_column zur Verfügung.
add_column
Der erste Parameter von add_column gibt den Tabellennamen an, gefolgt von dem Namen des neuen Feldes und dem Datentyp. Der Datentyp String wird für Zeichenketten verwendet. Dieser Befehl wird später von Rails in einen SQL-Befehl für das eingestellte Datenbanksystem umgewandelt.
Es gibt auch Befehle, mit denen Sie Felder ändern und löschen sowie neue Tabellen erstellen können (siehe Abschnitt 10.9).
Die Methode self.down
Rails bietet sogar die Möglichkeit, Änderungen am Datenbankschema wieder rückgängig zu machen. Dies läuft allerdings nicht ganz automatisch ab. Sie müssen nämlich Rails mitteilen, wie die Befehle, die Sie in der Methode self.up eingefügt haben, rückgängig gemacht werden.
Für diesen Zweck gibt es die Methode self.down . Tragen Sie hier den gegenteiligen Befehl ein. Um z. B. eine Spalte wieder zu entfernen, können Sie den Befehl remove_column einsetzen.
In unserem Beispiel haben wir nur einen Änderungs-Befehl verwendet. In der Praxis werden meist mehrere Befehle eingesetzt. Im Abschnitt kap_activerecord_migration_befehle auf Seite kap_activerecord_migration_befehle werden sämtliche Befehle vorgestellt.
Es gibt Fälle, in denen Sie keinen Befehl in der Methode self.down eintragen können, weil dieser nicht umkehrbar ist. Verwenden Sie in diesem Fall den Befehl raise IrreversibleMigration . Dieser sorgt dafür, dass beim Versuch, diese Migration rückgängig zu machen, eine Fehlermeldung ausgegeben wird.
Änderungen ausführen 

rake db:migrate
Nachdem wir nun ein Migration-Skript angelegt und bearbeitet haben, stellt sich die Frage, wie wir diese Änderungen auf die Datenbank anwenden können.
Ganz einfach! Mit dem Befehl
rake db:migrate
werden die noch nicht ausgeführten Migration-Skripte im Verzeichnis db/migrate ausgeführt.
Eine wichtige Frage drängt sich noch auf: Wie verhindert Rails, dass bereits ausgeführte Migration-Skripte erneut ausgeführt werden?
Dazu verwendet Rails einen Trick. Beim ersten Aufruf von dem Befehl rake db:migrate erstellt es eine Tabelle namens schema_info, in der die Version des zuletzt ausgeführten Migration-Skripts gespeichert wird. Nach dem ersten Aufruf enthält die Tabelle den Wert 1. Wenn z. B. nach dem letzten Ausführen von rake db:migrate zwei Migration-Skripte mit den Nummern 002_* und 003_* hinzugefügt wurden, so führt ein erneuter Aufruf des Befehls zum Ausführen dieser beiden Migration-Skripte, und in der Tabelle schema_info wird der Wert auf 3 gesetzt.
Die Tabelle schema_info verwaltet die Version.Änderungen rückgängig machen 

Jede Textverarbeitung bietet eine Undo-Funktion, mit der die letzten Änderungen rückgängig gemacht werden können. Ähnlich verhält es sich mit den Migration-Skripten in Rails. Mit dem Befehl
rake db:migrate VERSION=Nummer
schema_info
werden alle Migration-Skripte bis zur angegebenen Version rückgängig gemacht. Wenn z. B. drei Migrationen (001_*, 002_*, 003_*) vorhanden sind und bereits ausgeführt wurden und anschließend der Befehl rake db:migrate VERSION=1 ausgeführt wird, so werden die Änderungen aus den Dateien 003_* und 002_* rückgängig gemacht. Dazu führt Rails jeweils die Befehle in der Methode self.down der Dateien 003_* und 002_* absteigend hintereinander aus. Außerdem wird die Versionsnummer in der Tabelle schema_info aktualisiert.
Die Tabelle schema_info verwaltet die Version.Um sämtliche Migration-Schritte rückgängig zu machen, geben Sie als Versionsnummer »0« an.
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.