Displaying articles with tag migration

Ruby on Rails :: Migration

Posted by PunNeng, Sun Feb 11 22:24:00 UTC 2007

มาถึงอีกส่วนสำคัญของ Rails คือ migration

มาลองนึกถึงปัญหาบางอย่างที่เคยเจอ ปกติเวลาที่พัฒนางานไปแล้ว เกิดอยากจะย้าย database platform ขึ้นมา ก็ทำการ dumb ซะ จะได้ .sql มาตัวนึง(ในกรณีที่ export ออกมาเป็น file) คำสั่ง หรือ keyword ที่ต้องใช้ใน .sql นี้ อาจจะใช้ได้ใน platform นึง แต่อีก platform นึง อาจจะใช้ไม่ได้ ที่ผมเคยใช้คือ MySQL จะมี option ให้ใช้คือ -compatible ส่วนตัวอื่น ผมมะรู้ มะเคยใช้ แต่นั่นก็หมายความว่า เราจะต้องมี .sql นี้หลายๆ ตัว ในหลายๆ ตัว สำหรับหลายๆ database platform

หรือปัญหาของ DTS ของพี่คนนี้

ตัว Migration ของ rails ขจัดปัญหาพวกนี้ไปได้ครับ ตรงตาม DRY concept เป๊ะ ทำทีเดียว(ใช้ได้กับตัวอื่นๆ) อย่าทำหลายๆ ครั้ง

แล้ว Migration ทำอะไรได้บ้าง

  • Adding a new table
  • Removing a table
  • Adding a column to an existing table
  • Removing a column
  • Renaming a column
  • Renaming a table
  • Adding an index
  • Adding a primary key
  • Adding a foreign key

ถ้าชอบแบบ vdo สามารถดูได้จาก ในหัวข้อ Evolving your database schema without a sweat ที่พี่ DHH แสดงไว้ให้ดูนะครับ

แต่ถ้าเอาแบบภาษาไทย ทนอ่านหน่อยนะครับ :)

หลังจากที่ลองทำอันนี้แล้ว ก็มาทำต่อเลย แต่เราจะทำการลบ product table ที่สร้างไว้ก่อน จากน้ันไปที่ db/migrate/001_create_products.rb แล้วแก้ไขข้างในให้เป็น

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
class CreateProducts < ActiveRecord::Migration
  def self.up
    create_table :products do |t|
      t.column :name, :string, :limit => 128, :null => false
      t.column :description, :text
    end
  end

  def self.down
    drop_table :products
  end
end

file นี้ อันที่จริงมาจากตอนที่เราสั่ง generate scaffold_resource หรือจะมาตอนที่เรา generate model หรือสั่งสร้างโดยตรงที่ generate migration มาดูกันตั้งแต่ชื่อเลยละกัน 001_create_products.rb ตัวเลข 001 คือลำดับการทำงาน ส่วนที่ตามมาคือชื่อที่มันจะทำการผูกกับ class name ซึ่งในนี้เป็น CreateProducts(ดูความสัมพันธ์ของชื่อกับ class name เองนะครับ) ส่วนใน file ตัว create_table จะเป็นตัวสั่งสร้าง table ซึ่งสามารถเพิ่ม argument ได้เป็น

create_table :products, :force => true do |t|

ซึ่ง :force => true จะเป็นตัวที่หน้าบอกว่าให้ drop table ก่อนสร้าง(ถ้ามี) แล้วถัดมาก็จะเป็น

t.column :name, :string, :limit => 128, :null => false

บรรทัดนี้คือให้สร้าง column เชื่อว่า name ชนิด string มี length เป็น 128(varchar(128)) และห้ามเป็น null

มาลองสังเกตดู จะเห็นว่ามันมี self.up และ self.down สองตัวนี้ ไว้จัดการกับการ up และ down version ของ schema (มาลองทำดูก่อน แล้วจะมาอธิบายอีกที)

หลังจากที่แก้ไขไฟล์นี้เสร็จแล้ว ให้เปิด terminal(หรือ cmd) ขึ้นมา เข้าไปที่ project นี้ แล้วพิมพ์ว่า

project_name$ rake db:migrate

ควรจะได้หน้าตาแบบนี้

ลองไปดูใน phpmyadmin มันต้องมี products table ขึ้นมา ทีนี้ ลองเข้าไปดูใน db/schema.rb ด้วย มันจะมีชุด schema รวมอยู่ หน้าตาก็อย่างที่เห็น คือเอามารวมไว้ว่ามี table อะไรบ้าง และจะมีเลข version อยุ่ นั่นก็คือ เลขที่อยู่ที่อยู่หน้า migration file นั่นแหละ ว่าตัวล่าสุดที่เราทำไปถึงไหนแล้ว

อ้อ ผมเพิ่งนึกมาขึ้นมาได้ว่า ในแต่ละ product ควรจะต้องมี category ของมันด้วย โดย 1 category มีได้หลาย products โดยผมจะเพิ่ม category_id ไปใน product table ในการทำงานจริงๆ หลังจากที่ทำการ deploy ไปเรียบร้อยแล้ว เราทำการใส่ข้อมูลเสร็จแล้ว ถ้าหากต้องการแก้ไข schema ของ db วิธีแรก คือการเพิ่ม category_id เข้าไปใน 001_create_products.rb แล้วทำการ migrate ใหม่ ซึ่งสามารถทำได้โดยการ drop ตารางนี้ หรือ down version ไปที่ 0 แล้วข้อมูลล่ะ หายไปแน่นอน เราจะแก้ปัญหานี้ด้วยการเพิ่ม column ดีกว่าที่จะไปล้างมัน แล้ว migrate ใหม่ ย้อนกลับไปที่ terminal ใหม่ ให้พิมพ์ว่า

product_name$ ruby script/generate migration add_category_id_to_product

migration file จะถูกสร้างขึ้นมา ให้ไปดูใน db folder จะเจอ 002_add_category_id_to_products.rb เปิดขึ้นมาแล้วแก้ไขดังนี้

  1
  2
  3
  4
  5
  6
  7
  8
  9
class AddCategoryIdToProducts < ActiveRecord::Migration
  def self.up
add_column :products, :category_id, :integer end

def self.down
remove_column :products, :category_id end end

ก็คือการเพิ่ม column แล้วก็ต้องเอาออกด้วย ใน self.down จากนั้นก็สั่งใหม่ว่า

project_name$ rake db:migrate

คราวนี้ก็ลองเข้าไปตรวจดูใน database ว่ามันมาหรือยัง ดูใน db/schema.rb ด้วย ว่ามีอะไรเปลี่ยนแปลง ที่เปลี่ยนแปลงคือจะ update ตัว schema เป็นตัวล่าสุด และเปลี่ยนตัวเลข version เป็น 2 เพราะเรารันตัวที่สองไปแล้วนั่นเอง ทีนี้ ถ้ามีปัญหา เกิดอยากย้อนไป version 1 ด้วยปัญหาอะไรก็ตามแต่(ส่วนใหญ่จะย้อนไปเพื่อแก้ bug และปรับปรุง schema ก่อน deploy จริง) ก็สามารถทำได้โดย

project_name$ rake db:migrate VERSION=1

แล้วทุกอย่างก็จะกลับไปสูู่สภาพตอนที่อยู่ใน version 1 นั่นเอง หรือจะย้อนไป 0 ก็ได้ หรือจะขึ้นไปยัง version ที่สูงกว่านี้ก็ได้(ถ้ามี) นี่เป็นสาเหตุว่าทำไมต้องมี up และ down และจะต้องตรงกันข้ามกันเสมอ

จะใช้ migration ตอนไหน?? ก็ตอนที่เราจะต้อง deploy งาน ซึ่งจะใช้ร่วมกับ Capistrano ซึ่งเป็น Automating Application Deployment ต้องใช้ร่วมกัน แต่ตอนนี้ที่เห็นๆ คือ เกิดอยากย้ายเครื่องละ เครื่องมีปัญหา ก็เอา code นี้แหละ แล้วก็รันตามนี้แหละ แค่ db:migrate นี่แหละ ไปรันเครืองอื่น(กระบวณการนี้ แนะนำ Subversion ครับ) ก็เสร็จแล้วในการสร้าง schema

ยังมีคำสั่งตัวอื่นที่น่าจะได้ใช้

  • rake db:schema:dump ไว้ dumb ตัว schema ลงใน schema.rb ในกรณีที่เราไม่ได้ใช้ migration มาก่อน
  • rake db:schema:load ไว้ load ตัว schema ตัวที่ได้จากคำสั่งข้างบน ไปใช้ใน db ที่อื่นอีกที เราก็แค่รันคำสั่งนี้ แล้วมันก็จะไปสร้างตาม schema เขาเรานั่นแล
  • rake db:migrate VERSION=n อย่างที่ทำให้ดู ไว้กระโดดไปที่ version ตัวอื่น
  • rake RAILS_ENV=production db:migrate เอาไว้สั่งงานให้ migrate ตัว production เลย ไม่ใช้ development แล้ว
  • rake db:structure:dump ก็เอาไว้สร้าง .sql ขึ้นมา และ
  • rake --task เพื่อดูว่ามีคำสั่งอะไรบ้าง สำหรับ rake

แล้ว rake ล่ะ ทุกคำสั่งทำไมต้อง rake ครับ เราต้องรู้จักก่อน เพราะจะเจอกันอีกนาน มันคือ tool ตัวนึง ทำหน้าที่รันคำสั่ง ต่างๆ ตามที่เราทำมา ซึ่งคำสั่งพวกนี้จะถูกสร้างจากภาษา C ไว้จะลงเรื่อง rake กันอีกที

ปล. ศึกษาเพิ่มเติมได้ที่ http://api.rubyonrails.org/

แก้ไขล่าสุด วันที่ 31 กรกฏาคม 2550 เวลา 22.38 น.

0 comments | Filed Under: Ruby on Rails | Tags: migration

codegent: we're hiring