Rozwijanie aplikacji to nie tylko wdrażanie nowych funkcji czy wprowadzanie poprawek. Czasami trzeba cofnąć zmiany i wrócić do poprzedniego etapu projektu. Częstym problemem pojawiającym się podczas pracy na wielu gałęziach jest utrzymanie odpowiedniej wersji struktury bazy danych. Programiści korzystający z railsów mają do dyspozycji gotowe rozwiązania. Rozwiązania te wspierają implementację i kontrolę zmian w bazie danych - są to migracje. Nie będę opisywał jak to działa i jakie daje możliwości - chciałbym skupić się na problemie utrzymania odpowiedniej wersji struktury bazy danych podczas przełączania gałęzi.
Warstwa bazodanowa aplikacji jest odrębnym bytem i jest kontrolowana wyłącznie przez migracje. Tworząc nowe migracje należy pamiętać, aby planowana transformacja struktury bazy danych była odwracalna. Oczywiście w skrajnych przypadkach możemy podnieść IrreversibleMigration
w w dół metoda. Poinformuje nas to również o tym, że migracji nie można cofnąć. Istnieją różne rodzaje zmian, które wprowadzamy podczas migracji - tworzenie, modyfikowanie i usuwanie tabel, kolumn lub indeksów. Operacje usuwania i modyfikowania są najbardziej wrażliwe na zmiany. Dlaczego? Rozważmy następujący scenariusz:Pracujemy z gałęzią master, która jest naszą główną ścieżką roboczą. Obecnie mamy jedną tabelę:
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :name
t.text :description
t.string :status, null: false
t.timestamps null: false
end
end
end
Nasz Artykuł
model ma takie walidacje, które wymagają obecności nazwa, opis i status atrybuty.
class Artykuł < ActiveRecord::Base
waliduje :name, obecność: true
validates :description, presence: true
validates :status, presence: true
end
Wdrażamy zmiany w naszych artykuły tabela na cecha i usuwamy gałąź rozwojową status kolumna.
class RemoveStatusColumnFromArticles < ActiveRecord::Migration
def change
remove_column :articles, :status, :string
end
end
Przeprowadzamy migrację:
$ [example/feature]: bundle exec rake db:migrate
== 20150605120955 RemoveStatusColumnFromArticles: migracja ===================
-- remove_column(:articles, :status, :string)
-> 0.0717s
== 20150605120955 RemoveStatusColumnFromArticles: zmigrowano (0.0718s) ==========
Schemat bazy danych ulega zmianie:
diff --git a/db/schema.rb b/db/schema.rb
index 2a100a9..76438c1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,14 +11,13 @@ #
Zdecydowanie zaleca się sprawdzenie tego pliku w systemie kontroli wersji.
-ActiveRecord::Schema.define(version: 20150605120350) do
+ActiveRecord::Schema.define(version: 20150605120955) do
createtable "articles", force: :cascade do |t|
t.string "name"
t.text "description"
- t.string "status", null: false t.datetime "createdat", null: false
t.datetime "updated_at", null: false
end
end
Następnie zatwierdzamy zmiany w pliku cecha branch. Aby zasymulować ten problem, przełączamy bieżącą gałąź na master. Struktura bazy została zmieniona przez migrację, która usuwa sekcję status w kolumnie cecha branch. Spróbujmy użyć następującego polecenia:
Article.create!(name: "Kaboom", description: "Lorem ipsum...", status: "active")
Co się stanie po wykonaniu powyższego kod? Błąd: ActiveRecord::UnknownAttributeError: nieznany atrybut "status" dla artykułu
a to z powodu niekompatybilnej wersji struktury bazy danych. Przed zmianą gałęzi na master powinniśmy cofnąć migrację, która usuwa bazę danych status z kolumny Artykuł tabela.
Co możemy zrobić, aby sprawdzić, czy musimy cofnąć niektóre migracje przed przełączeniem gałęzi? Za pomocą systemu kontroli wersji (tutaj jest to git) możemy sprawdzić naszą pracę, tworząc pomocny alias:
~/.gitconfig
[alias]
migrations = "!f() { git diff --name-only $1..$2 db/migrate | tr -d '[A-Za-z/_.]'; }; f"
Wykonywanie git migrations master feature
Polecenie spowoduje wyświetlenie listy wersji migracji, które znajdują się na stronie cecha i których nie można znaleźć na mistrz.
$ [przykład/funkcja]: główna funkcja migracji git
20150605120955
Dzięki tym informacjom możemy łatwo cofnąć zmiany dokonane w strukturze bazy danych przed przełączeniem na master.
$ [example/feature]: bundle exec rake db:migrate:down VERSION=20150605120955
== 20150605120955 RemoveStatusColumnFromArticles: przywrócenie ===================
-- add_column(:articles, :status, :string)
-> 0.0009s
== 20150605120955 RemoveStatusColumnFromArticles: przywrócono (0.0045s) ==========
Jeszcze jedną rzeczą, którą powinniśmy zrobić po wycofaniu migracji, jest przywrócenie stanu schematu bazy danych.
$ [example/feature]: git status
Na gałęzi funkcji
Zmiany nie zostały zatwierdzone:
(użyj "git add ...", aby zaktualizować to, co zostanie zatwierdzone)
(użyj "git checkout -- ...", aby odrzucić zmiany w katalogu roboczym)
zmodyfikowano: db/schema.rb
brak zmian dodanych do zatwierdzenia (użyj "git add" i/lub "git commit -a")
$ [example/feature]: git checkout db/schema.rb
$ [example/feature]: git status
Na gałęzi funkcji
nic do zatwierdzenia, katalog roboczy czysty
Teraz możemy łatwo przełączyć się na mistrz oddział.
Podany przykład nie jest skomplikowanym problemem do rozwiązania, ale powinien pomóc ci uświadomić sobie, jak ważne jest utrzymanie struktury bazy danych podczas zmiany kontekstu pracy.