作為一名優秀的程序員,除了編寫大量的程序外,創建大量的數據庫也是在所難免的。在過去的幾年中,筆者曾經創建和管理了大量的MySQL數據庫,在這個過程中曾使用各種工具來進行管理,以便使它的過程更簡單,例如首選的圖形化管理工具PHPMyAdmin,以及基於命令行的MySQL clent,它們都非常好用。但是,在筆者內心身處始終覺得,我們是一名程序員,而不是一名數據庫管理員,總感覺編程和數據庫管理之間有些跨越。為什麼不用與編程相同的方式來管理數據庫結構呢?自從開始使用Rails,終於找到了答案。通過Rails的功能,可以使用程序員的方式來管理MySQL數據庫了。
一、使用Migrations管理數據表
在Rails中,當創建一個叫contact的model時,同時一個名為contacts的數據表將會被創建。因此,對數據表contacts的操作可以轉換成對模型contact操作,可以訪問它的屬性。而很多的新手習慣使用一些框架什麼的來操作數據表,其實,可以通過Rails本身提供的功能就可以操作數據表了。這樣的功能就是Migrations功能。
多數Rails開發者使用Migrations遷移的基本功能來創建和管理數據庫。數據遷移功能讓你可以使用Ruby語言來管理數據庫方案,可以充分利用一些Ruby所特有的工具,諸如Rake,來根據Ruby腳本提供的指令來更新數據庫。還有,數據遷移功能還具有一個內置的版本控制功能,可以像在Subversion或CVS中那樣對所做的修改進行前後的回滾。聽起來是不是很具有誘惑力呢?
Migrations有點像活動記錄(Active Record,一個對象,它包裝數據庫表或視圖中的某一行,封裝數據庫訪問,並在這些數據上增加了領域邏輯),可以通過Migrations進行程序形式的管理數據表,即可以創建、修改、刪除表格,而且語法很簡單。更重要的是,Migrations提供了一個構建的控制器。
事實上,當在Rails下創建一個新的model時,會自動的創建Migration文件。例如,創建一個contact模型時,在項目的db目錄下,即可發現一個名為001_create_contacts.rb的文件,其內容如下所示:
class CreateContacts < ActiveRecord::Migration def self.up create_table :contacts do |t| end end def self.down drop_table :contacts end end
如果想要創建數據表,可以把以上內容修改成如下內容:
class CreateContacts < ActiveRecord::Migration def self.up create_table :contacts do |t| t.column :name, :string, :null => false t.column :email, :string t.column :phone, :string, :limit => 10, :null => false end end def self.down drop_table :contacts end end
現在可以使用Migration功能了,在項目的目錄下運行如下的Rake命令:
%>rake db:migrate
現在登陸MySQL數據庫中,可以看到,contacts表已經創建好了。那麼,如果想撤消剛才的創建要怎麼辦呢?要回滾上面的操作可以使用VERSINO選項。因為數據移植功能的工作原理與版本控制工具很相似,你可以將數據庫回滾到一個早期的版本。版本號是由數據移植腳本所提供的數字來確認的:
%>rake db:migrate VERSION=0
再次登陸MySQL數據庫,可以看到contacts表已經刪除掉了。此外,如果想進行其它一些更加復雜的操作時,自然而然想到通過創建另一個migrations。例如,想另外創建一張表,或修改字段的數據類型等。可能在項目的目錄下運行如下的代碼來創建一個新的migration文件:
%>ruby script/generate migration your_desired_migration_name
二、從Fixture中加載數據
在創建一個應用程序時,我們往往遇到這樣的情況,手邊已經有了一些數據,需要將這些現成的數據融合到應用程序的數據庫中去。例如,在進行客戶的結帳時,往往需要詢問客戶,以確定他們居住在哪個州。因為州名可能在程序中很多地方使用到,於是就有必要創建一個名為state的model。但這裡又不想手工的創建這樣的表格,因為這樣做不只是很煩瑣,更重要的是可能導致錯誤。
這個時候,就可以使用fixtures這一功能了。它最初的目的,是為了在進行程序測試時,簡化從示例數據向測試數據的轉換工作。Fixture是包含運行測試時使用數據的文件。但是,稍微加工一下,我們也可使用它們在migration遷移期間來加載數據。
但後來的事實證明,這是一種很不錯的方法,可以使用種子數據生成測試的Model,例如前面所提到的州名。所需要做的工作就是把數據按照fixtures的格式進行組織,這裡的格式要求有兩點:YAML(YAML Ain't Markup Language,這是一種數據序列化(serialization)語言,是一種可讀的文本的數據結構,它的設計目標是使人們容易讀,程序容易處理。它類似XML,但是比XML簡單)及comma-separated(逗號分隔)。
下面將展示如何通過逗號分隔的fixture文件,用於保存州名,並且使用Rake來生成包含這些數據的數據表。這是假定讀者已經創建了state model及migration文件,接下來生成數據表。
首先,請記住Rails的編輯習慣,因此這裡應該在項目的test/fixtures目錄下創建我們的fixtures,因為這些數據最主要的用途還是用來進行測試。當然了,在項目的db目錄下面還可以創建seed目錄,用於存放種子數據。
然後,創建state.csv文件,並存放在db/seed目錄下面。該文件的內容如下:
id, name, abbreviation
1, Alabama, AL
2, Alaska, AK
3, Arizona, AZ
4, Arkansas, AR
5, California, CA
6, Colorado, CO
接下來,需要創建一個Rake文件,至於這一點,要想完全講清楚,沒有一篇比較長的文章很難。因此在這裡不進行具體的解釋,只列出步驟。創建seeder.task文件,並將它存放在項目的lib/tasks目錄下面,該文件的內容如下所示:
namespace :db do desc "Load seed fixtures (from db/seed) into the current environment's database." task :seed => :environment do require 'active_record/fixtures' Dir.glob(RAILS_ROOT + '/db/fixtures/*.csv').each do |file| Fixtures.create_fixtures('db/seed', File.basename(file, '.*')) end end end
為了生成states數據表,需要在項目的目錄下執行如下的命令:
%>rake db:seeder
登陸MySQL數據庫服務器,檢查數據庫,表已經創建好了。
三、在數據庫之間遷移數據
Rails的開發者Tobias Lutke遇到了數據遷移的問題,他采用Rake任務將數據庫中的數據生成YAML格式的文件,再通過Rails的Migrations功能來生成其它數據庫(包括MySQL、SQLite、SQL Server以及Oracle)。在這裡,不列出具體的代碼,因為很長,只列出一些關鍵的命令就可以了。
執行下面的命令,把數據庫中的數據生成YAML格式:
%>rake db:backup:write
於是,數據庫中的所有表都備份到了db/backup目錄中了。接下來要做的就是修改database.yml文件,以指向接收數據的數據庫。並運行如下的命令:
%>rake db:backup:write
四、小結
作為一個Rails開發者,你可能熟練得運用“rake”運行你的測試,或者你利用“rake db:migrate”運行你的數據遷移任務(migration)。但是,你是否真正明白在那些Rake任務的背後發生了什麼嗎?你是否意識到,你可以編寫屬於你的任務,或者創建好用的屬於你的Rake庫文件?
本文只是介紹了Rails的數據管理功能的冰山一角,事實上,它不僅支持MySQL,還支持目前市面上的大多數數據庫,諸如SQLite、PostgreSQL、SQL Server和Oracle。如果讀者想了解更多這個精彩工具的功能,可以去查看Rails的官方站點的migrations documentation(http://api.rubyonrails.org/classes/ActiveRecord/Migration.html)。