程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Rails開發細節(五)Migrations 數據遷移

Rails開發細節(五)Migrations 數據遷移

編輯:關於JAVA

1.簡介

在rails中用migration可以很方便的管理數據庫的結構。可以創建數據庫,創建表,刪除表,添加字段,刪除字段,整理數據。

migration就是一系列的class,這些類都繼承了ActiveRecord::Migration類。

class CreateProducts < ActiveRecord::Migration 
  def up 
    create_table :products do |t| 
      t.string :name 
      t.column :description, :text 
      t.timestamps 
    end 
  end 
     
  def down 
    drop_table :products 
  end  
end

上面就是一個migration例子。up方法中的代碼會在

rake db:migrate

之後執行。

down方法中的代碼會在

rake db:rollback

之後執行。

t.timestamps會自動產生created_at和updated_at列。

還可以進行表結構修改。

class AddReceiveNewsletterToUsers < ActiveRecord::Migration 
  def up 
    change_table :users do |t| 
      t.boolean :receive_newsletter, :default => false
    end 
    User.update_all ["receive_newsletter = ?", true] 
  end 
      
  def down 
    remove_column :users, :receive_newsletter 
  end 
end

rails3.1之後產生了一個新的方法change,主要用來創建表和列,不用寫一對up和down了,使用rake db:rollback回滾的時候數據庫不用down方法也知道如何做了。

1.1.migration提供了很多的方法

add_column

add_index

change_column

change_table

create_table

drop_table

remove_column

remove_index

rename_column

如果想回滾migration對數據庫造成的改變,可以使用rake db:rollback命令。

1.2.ActiveRecord支持的列類型

:binary

:boolean

:date

:datetime

:decimal

:float

:integer

:primary_key

:string

:text

:time

:timestamp

2.創建migration

2.1.創建model

rails generate model Product name:string description:text

創建的migration文件位於db/migrate目錄,文件名稱為yyyymmddmmss_create_products.rb。

class CreateProducts < ActiveRecord::Migration 
  def change 
    create_table :products do |t| 
      t.string :name 
      t.text :description 
      
      t.timestamps 
    end 
  end 
end

2.2.創建單獨的migration

rails generate migration AddPartNumberToProduct

class AddPartNumberToProducts < ActiveRecord::Migration 
  def change 
  end 
end

指定列的名稱

rails generate migration AddPartNumberToProduct part_number:string

class AddPartNumberToProducts < ActiveRecord::Migration 
  def change 
    add_column :products, :part_number, :string
  end 
end

刪除列

rails generate migration RemovePartNumberToProduct part_number:string

class RemovePartNumberFromProducts < ActiveRecord::Migration 
  def up 
    remove_column :products, :part_number 
  end 
     
  def down 
    add_column :products, :part_number, :string
  end 
end

還可以添加多個列

rails generate migration AddDetailsToProducts part_number:string price:decimal
     
     
class AddDetailsToProducts < ActiveRecord::Migration 
  def change 
    add_column :products, :part_number, :string
    add_column :products, :price, :decimal
  end 
end

3.編寫mirgation

3.1.創建表

create_table :products do |t| 
     
  t.string :name 
     
end 
     
create_table :products do |t| 
     
  t.column :name, :string, :null => false
     
end

如果數據庫是mysql,還可以通過下面的語句指定使用的引擎,mysql默認的引擎是InnoDB。

create_table :products, :options => "ENGINE=MyISAM" do |t| 
  t.string :name, :null => false
end

3.2.修改表結構

change_table :products do |t| 
  t.remove :description, :name 
  t.string :part_number 
  t.index :part_number 
  t.rename :upccode, :upc_code 
end

刪除name,description字段,添加part_number字段,在part_number字段建立索引,重命名upccode為upc_code。

3.3.輔助工具

t.timestamps可以自動添加created_at 和 updated_at列。

#創建表的同時添加 
create_table :products do |t| 
  t.timestamps 
end 
     
#給已經存在的表添加 
change_table :products do |t| 
  t.timestamps 
end

還有一個幫助工具references,用來指明表的外鍵關系。

create_table :products do |t| 
  t.references :category 
end

上面的代碼會在products表中添加一個外鍵字段category_id。

create_table :products do |t| 
  t.references :attachment, :polymorphic => {:default => 'Photo'} 
end

上面的代碼不僅會在products表中添加外鍵字段attachment_id,還會添加attachment_type字段,string類型,默認值是Photo。

3.4.change方法的使用

change方法可以部分的替代up和down方法,數據庫會自動的回滾。但是目前在change方法中只支持下面的migration。

add_column

add_index

add_timestamps

create_table

remove_timestamps

rename_column

rename_index

rename_table

如果要用其他的migration,你就需要自己寫up和down了,不能再使用change了。

3.5.up和down方法的使用

down方法用來回滾數據庫,你需要注意在down方法中的順序,相對於up方法中的順序。

class ExampleMigration < ActiveRecord::Migration 
  def up 
    create_table :products do |t| 
      t.references :category 
    end 
    #add a foreign key 
    execute <<-SQL 
      ALTER TABLE products 
        ADD CONSTRAINT fk_products_categories 
        FOREIGN KEY (category_id) 
        REFERENCES categories(id) 
    SQL 
    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address 
  end 
      
  def down 
    rename_column :users, :email_address, :email 
    remove_column :users, :home_page_url 
    execute <<-SQL 
      ALTER TABLE products 
        DROP FOREIGN KEY fk_products_categories 
    SQL 
    drop_table :products 
  end 
end

4.運行遷移

通過

rake db:migrate

命令就可以執行遷移任務。

如果指定了版本,就會執行指定版本的up,change或者down方法。版本就是migration文件的前綴。用VERSION參數來指定版本號。

rake db:migrate VERSION=20080906120000

拿上面的這個命令舉例。如果20080906120000 比當前的版本大,會執行所有migration的up方法,包括20080906120000 中的up方法,不執行其他的migration方法。如果是小於,會執行所有migration的down方法,不包括20080906120000中的down方法。

4.1.回滾數據庫

回滾上一次的數據庫變更

rake db:rollback

回滾之前三次的數據庫變更

rake db:rollback STEP=3

重做之前三次的數據庫變更,重做之前會先回滾。

rake db:migrate:redo STEP=3

4.2.重置數據庫

刪除當前數據庫,重新創建,重新執行migration。

rake db:reset

4.3.執行指定的migration

rake db:migrate:up VERSION=20080906120000

4.4.改變執行migration之後的提示信息

class CreateProducts < ActiveRecord::Migration 
  def change 
    suppress_messages do
      create_table :products do |t| 
        t.string :name 
        t.text :description 
        t.timestamps 
      end 
    end 
    say "Created a table"
    suppress_messages {add_index :products, :name} 
    say "and an index!", true
    say_with_time 'Waiting for a while' do
      sleep 10 
      250 
    end 
  end 
end

5.在migration中使用model對象

假設有兩個開發者,公用一個代碼庫。

其中一個休假了,另外一個還在工作,對數據庫進行了修改。

# db/migrate/20100513121110_add_flag_to_product.rb 
      
class AddFlagToProduct < ActiveRecord::Migration 
  def change 
    add_column :products, :flag, :boolean 
    Product.all.each do |product| 
      product.update_attributes!(:flag => 'false') 
    end 
  end 
end 
     
# app/model/product.rb 
      
class Product < ActiveRecord::Base 
  validates :flag, :presence => true
end 
     
# db/migrate/20100515121110_add_fuzz_to_product.rb 
      
class AddFuzzToProduct < ActiveRecord::Migration 
  def change 
    add_column :products, :fuzz, :string
    Product.all.each do |product| 
      product.update_attributes! :fuzz => 'fuzzy'
    end 
  end 
end 
     
# app/model/product.rb 
      
class Product < ActiveRecord::Base 
  validates :flag, :fuzz, :presence => true
end

添加了一些字段,添加了一些驗證。

休假的同事回來了。

獲取代碼。

執行 rake db:migrate。

報錯了。

rake aborted! 
An error has occurred, this and all later migrations canceled: 
      
undefined method `fuzz' for #<Product:0x000001049b14a0>

因為在執行完第一個migration之後,驗證model的時候,還沒有執行第二個migration,也就還沒有fuzz字段。

解決的辦法就是在migration中添加空的model定義,阻止migration運行的時候驗證model,因為空的model中沒有驗證規則,這樣就migration就可以運行完畢。

由於在migration中包括更新字段的操作,還需要添加Product.reset_column_information來更新ActiveRecord中緩存的之前的空Product就可以了。

# db/migrate/20100513121110_add_flag_to_product.rb 
      
class AddFlagToProduct < ActiveRecord::Migration 
  class Product < ActiveRecord::Base 
  end 
      
  def change 
    add_column :products, :flag, :integer 
    Product.reset_column_information 
    Product.all.each do |product| 
      product.update_attributes!(:flag => false) 
    end 
  end 
end 
     
     
# db/migrate/20100515121110_add_fuzz_to_product.rb 
      
class AddFuzzToProduct < ActiveRecord::Migration 
  class Product < ActiveRecord::Base 
  end 
      
  def change 
    add_column :products, :fuzz, :string
    Product.reset_column_information 
    Product.all.each do |product| 
      product.update_attributes!(:fuzz => 'fuzzy') 
    end 
  end 
end

6.數據庫的描述文件

在 db/schema.rb或者是db/*.sql文件中就是數據庫的結構,這個文件不是用來修改的,自動生成,用來查看數據庫的當前狀態。

6.1.描述文件的類型

在config/application.rb文件中可以配置描述文件的類型,config.active_record.schema_format,值為 :sql 或 :ruby,默認是:ruby。如果是:ruby,文件的內容就是。

ActiveRecord::Schema.define(:version => 20080906171750) do
  create_table "authors", :force => true do |t| 
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end 
      
  create_table "products", :force => true do |t| 
    t.string   "name"
    t.text "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string "part_number"
  end 
end
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved