在上一篇文章裡稍微提到了ActiveRecord能讓你不需寫SQL語法便能快速地存取資料,(事實上我只提到了讀取的部份,寫入其實也相當簡單,依樣畫葫蘆,只是使用的method不同罷了)這篇便來談談在ActiveRecord裡,是如何處理Model與Model之間的關係。
定義Model之間的關係
平常我們在描述兩個資料表之間的關係,通常可能會說兩張table是一對一、一對多、或是多對多,而在ActiveRecord裡,兩個Model之間的關係主要以上述的三種方式來定義。
延續上一篇文章中我所建立的三個model: User, Video, and Comment,這三個model之間的關係如下:
- User has_many videos:User可以擁有多個Videos
- User has_many comments:User可以擁有多個Comments
- Video belongs_to user:Video必然會屬於某個User
- Comment belongs_to user:Comment也會屬於某個User
- Video has_many comments:每個影片都可以有多筆Comments
- Comment belongs_to video:每個 comment也會屬於某個Video
看這樣的描述,如果是尚未接觸過Ruby on Rails或是ActiveRecord的讀者一定會感到疑惑,根本聽不懂我在說什麼嘛!請看下列三段程式碼,分別是上述三個Model的程式:
#Model: rails_app/app/models/user.rb
class User < ActiveRecord::Base
has_many :videos
has_many :comments
end
#Model: rails_app/app/models/video.rb
class Video < ActiveRecord::Base
has_many :comments
belongs_to :user
end
#Model: rails_app/app/models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :video
end
看出什麼了嗎?只要使用很直觀的has_many, belongs_to這樣的method便可以輕鬆定義Model之間的關係。
定義資料表欄位:使用Migration
接著,我們還必須仰賴migration替我們新增對應的資料表或欄位,在上一篇文章中已經完成了User migration的程式:
#Migration: rails_app/app/db/migrate/001_create_users.rb
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :name, :string, :null => false
t.column :email, :string, :null => false
t.column :password, :string, :null => false
t.column :nickname, :string, :null => false
t.column :memo, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :users
end
end
在上一篇文章中,除了self.up、self.down兩個method之外,我並沒有交代其他程式碼所代表的意義,這邊稍微補充一下
- create_table代表建立一個新的資料表。透過對資料表進行管理的method尚有:deop_table, add_column, remove_column, rename_column等等。
- create_table :users代表建立一個名為users的資料表,其後開始是一個block
- t.column :name, :string, :null => false分別代表"table中的欄位"、"欄位名稱"、"資料型態"、"不可為null",這樣的寫法在Ruby是很常見的寫法,假設你不懂Ruby、但卻對Rails有興趣,我建議你可以透過在學習Rails的過程中慢慢熟悉Ruby語言的寫法,我相信很快就上手。
- 有關於建立欄位時的相關選項,可以參考Rails的文件,或是往後的文章或許會用到,我會再一一介紹。
- 別忘了created_at, updated_at是ActiveRecord中的Magic Column Name,它可以幫你在對資料表寫入資料的時候,自動產生資料建立或修改的時間。(雖然說寫入時間這種事情只要一行就可以解決)
接下來我們要繼續完成與Video、Comment這兩個model相關的migration。migration檔案通常是由generator在建立model的同時,就會自動新增一個migration的檔案,而generator也可以單獨產生一個migration file讓你對資料庫的schema進行改變。
#Migration: rails_app/app/db/migrate/002_create_videos.rb
class CreateVideos < ActiveRecord::Migration
def self.up
create_table :videos do |t|
t.column :title, :string, :null => false
t.column :description, :text
t.column :user_id, :integer
t.column :created_at, :datetime
end
end
def self.down
drop_table :videos
end
end
#Migration: rails_app/app/db/migrate/003_create_comments.rb
class CreateComments < ActiveRecord::Migration
def self.up
create_table :comments do |t|
t.column :content, :text
t.column :user_id, :integer
t.column :video_id, :integer
t.column :created_at, :datetime
end
end
def self.down
drop_table :comments
end
end
從上面的Migration可以發現下列兩點:
- Migrate目錄裡面的檔案,檔名會依001, 002, 003...的次序為開頭,這代表migration的版本。例如你開發到第10版時,發現第十版新增的table或欄位是有錯的,你可以馬上將資料庫的schema還原到第九版。
- 上一篇文中我有提到,Magic Column Name中包含了「xxxx_id」這樣的格式,因此在video、comment的table中可以看到出現「user_id」、「video_id」這樣的欄位,往後ActiveRecord便是用這個欄位來替你找到對應的資料(自動幫你join table)
將Migration定義的內容寫入資料庫
從上一篇文章透過generate指令來產生model、migration files,到本篇文章我們實際去修改上述的檔案內容,如果熟練大概三分鐘就可以完成,接下來便可以開始感受到ActiveRecord所帶來的高生產力與享受:D
做完上述動作之後,我們便要將定義好的table schema寫入資料庫裡面,在Rails的目錄結構裡面,config/database.yml便是在定義資料庫的相關設定值。設定資料庫的動作一般是在建立好Rails Project便該完成的,這算是開發Rails project的前置動作之一,那為甚麼我到現在才說咧?其實只是我忘了先講怎麼建立專案、怎麼設定資料庫罷了.....以後如果要把這個系列彙整起來給Rails新鮮人看,再寫一篇好了XD
關於Rails Project的前置動作可以參考午夜盧比人Winson寫的「My Rails Way」,裡面提到的技巧都相當實用,像是第一點提到的
為了節省時間,我通常會維護一個叫做sample的Rails project,裡面把簡易的帳號、密碼、登入做起來,以及設計一個model,也把他的tag、comment功能作起來,並把相關的測試案例寫好、常用的外掛裝起來,然後整包壓成一個sample.zip檔。
倘若資料庫已經設定好,接下來會使用到「rake」指令來將我們規劃好的schema寫入資料庫裡面,指令如下:
rake db:migrate
通常會得到下面的結果:
deduce@nccu:~/test$ rake db:migrate
(in /home/deduce/test)
== CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0170s
== CreateUsers: migrated (0.0174s) =============================
== CreateVideos: migrating =====================================
-- create_table(:videos)
-> 0.0090s
== CreateVideos: migrated (0.0094s) ============================
== CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0089s
== CreateComments: migrated (0.0093s) ==========================
如果沒有出現錯誤訊息就代表資料表皆成功建立,接下來我們便可以開始使用ActiveRecord來對資料庫進行各種關聯式的存取動作!
註:實在是有點拖稿,不過我寫這些文章是希望可以盡量淺白,所以盡量詳細、盡量簡單,一樣,有任何錯誤或建議請多指教。