Začínáme s Migracemi

03.09.2007 19:41

Skoro každé rozšiřování nebo upravování Rails aplikace nějak souvisí s databází. Většinou pouze přidáváme nějakou tu logiku do modelů a controllerů a databázi pouze používáme. Čas od času se ale databázové schéma mění. Na změny databázového schématu existuje spousta grafických a klikacích nástrojů, někdo používá konzoli databáze a píše SQL DDL příkazy ručně. Já mám rád Rails a ještě více Ruby. Migrace jsou nástroj jak změny v databázovém schématu definovat v Ruby. V tomto článku bych chtěl popsat jak migrace fungují, jak se používají a poradit jak migrace nasadit na již rozběhnutém projektu, kde se na ně zapomnělo.

Migrační teorie

Migrace jsou sobory popisující změnu v databázovém schématu. Technicky vzato jsou migrace třídy žijící v adresáři db/migrate vaší Rails aplikace. Všechny migrace jsou potomky třídy ActiveRecord::Migration, jejíž metody bohatě využívají. Každá migrace musí obsahovat classmetody up a down, proč to pochopíme až si vysvětlíme jak migrace fungují.

Migrace je nejlepší spouštět pomocí rake tasku db:migrate. Pokud chcete migrace spouštět ze svého kódu doporučuji přečíst si kód tohoto tasku, který najdete v railties/lib/tasks/databases.rake. Třída provádějící migrace je ActiveRecord::Migrator (bez dokumentace) a funguje tak, že porovná verzi databáze uloženou v tabulce schema_info a číslo poslední migrace v adresáři db/migrate a pokud najde nějaké migrace s číslem větším než je číslo databáze, tak tyto migrace jednu podruhé spouští.

Pro správnou funkčnost migrací jsou potřeba dvě věci:

  1. aby v databázi existovala tabulka schéma_info s jedním int sloupečkem version
  2. aby třídy migrací byly v souborech pojmenovaných xxx_nazev_migrace.rb, kde xxx je číslo migrace

Toto nás, ale nemusí trápit, protože pokud budeme používat Rails generátory a rake tasky tak se vše udělá jako obvykle samo.

Migrace v praxi

Nejzákladnějším použitím migrací je vytvoření nového modelu, pro který potřebujeme vytvořit tabulku. Model vytvoříme pomocí Rails generátoru script/generace model NazevModelu. Toto nám vytvoří modelovou třídu, unit test a migraci, která se bude jmenovat xxx_create_nazev_modelus.rb. Dejme tomu, že vytváříme model s názvem Item (jak originální) pomocí script/generace model Item a vytvoří se nám migrace 001_create_items.rb s poněkud nudným kódem pro vytvoření prázdné tabulky.

class CreateItems < ActiveRecord::Migration
def self.up
create_table :items do |t|
end
end def self.down
drop_table :items
end
end

Metoda up se volá, když se migruje nahoru z verze nižší na verzi vyšší. Metoda down se volá když se migruje dolů z verze vyšší na verzi nižší. Migrování dolů se dá docílit tak, že tasku db:migrate předáte parametr VERSION=x. Databáze se pak zmigruje zpět na verzi x. Všechny metody pro úpravy databáze najdete v dokumentaci třídy ActiveRecord::Migration. I když je předchozí migrace skoro prázdná, vytvořená tabulka by obsahovala sloupec id pro primární klíč. K čemu by nám byl model bez id, že jo? Kód pro vytvoření tabulky už nám Rails vygenerovaly přidáme tedy pár sloupečků a nějaké indexy.

class CreateItems < ActiveRecord::Migration
def self.up
create_table :items do |t|
t.column :name, :string
t.column :numeric, :integer, :null => false, :default => 1
t.column :description, :text
t.column :parent_id, :integer
end
add_index :items, :parent_id
end def self.down
drop_table :items
end
end

Teď už stačí pouze spustit rake db:migrate a tabulka se na nás směje v databázi.

Ejhle, změna

Další užitečný Rails generátor je generátor migrací. Přijde vhod, když prostě potřebujeme něco změnit. Zavoláním script/generace migration add_position_to_items se nám vygeneruje prázdná migrace, na které si předvedeme další možnosti migrací.

class AddPositionToItems < ActiveRecord::Migration
def self.up
# přidání sloupečku
add_column :items, :position, :integer
# přejmenování sloupečku
rename_column :items, :description, :desc
# přidání sloupečku
add_column :items, :short_desc, :integer
#
# načteme z DB nové atributy objektu
Item.reset_column_information
#
# aktualizace záznamů
Item.find(:all).each do |item|
# inicializace nového sloupečku
item.short_desc = item.desc[0, 20]
item.save
end
end def self.down
# vrátíme provedené změny
remove_column :items, :position
rename_column :items, :desc, :description
remove_column :items, :short_desc
end
end

O nasazení migrací na rozběhnuté projekty a dalších fíčurách snad příšte.

Komentáře

K tomuto postu je 0 komentářů. Přidej vlastní →

Přidej komentář

Povinná pole jsou vyznačena tučně.