v7.0.0
更多資訊請前往 rubyonrails.org: 更多在 Ruby on Rails

Active Record Migrations

Migrations 是 Active Record 的一個功能,它允許你發展你的 隨著時間的推移資料庫模式。而不是在純 SQL 中編寫模式修改, migrations 允許您使用 Ruby DSL 來描述對錶的更改。

閱讀本指南後,您將瞭解:

1 Migration 概述

Migrations 是一種方便的方式 隨著時間的推移改變你的資料庫架構 以一致的方式。他們使用 Ruby DSL,因此您不必 手動編寫 SQL,允許您的架構和更改獨立於資料庫。

您可以將每個 migration 視為資料庫的新“版本”。一個 模式開始時什麼都沒有,每個 migration 都會修改它以新增或 刪除表、列或條目。 Active Record 知道如何更新您的 沿著這個時間線的模式,從它在 歷史記錄到最新版本。 Active Record 也會更新您的 db/schema.rb 檔案以匹配資料庫的最新結構。

下面是一個 migration 的例子:

class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end
end

這個 migration 添加了一個名為 products 的表,其中有一個名為的字串列 name 和名為 description 的文字列。一個名為 id 的主要 key 列 也將被隱式新增,因為它是所有 Active 的預設主 key 記錄 models。 timestamps 宏添加了兩列,created_atupdated_at。這些特殊列由 Active Record 自動管理 如果它們存在。

請注意,我們定義了我們希望及時發生的變化。 在此 migration 執行之前,將沒有表。之後,表將 存在。 Active Record 也知道如何反轉這個 migration:如果我們滾動 將此migration 返回,它將刪除表。

在支援 transactions 和更改架構的語句的資料庫上, migrations 被包裹在一個 transaction 中。如果資料庫不支援這個 那麼當 migration 失敗時,成功的部分將不會被滾動 背部。您將不得不回滾手動所做的更改。

有些查詢不能在 transaction 中執行。如果你的 介面卡支援 DDL transactions 你可以使用 disable_ddl_transaction! 來 為單個 migration 禁用它們。

如果你希望 migration 做一些 Active Record 不知道的事情 要反轉,您可以使用 reversible

class ChangeProductsPrice < ActiveRecord::Migration[7.0]
  def change
    reversible do |dir|
      change_table :products do |t|
        dir.up   { t.change :price, :string }
        dir.down { t.change :price, :integer }
      end
    end
  end
end

或者,您可以使用 updown 代替 change

class ChangeProductsPrice < ActiveRecord::Migration[7.0]
  def up
    change_table :products do |t|
      t.change :price, :string
    end
  end

  def down
    change_table :products do |t|
      t.change :price, :integer
    end
  end
end

2 建立一個 Migration

2.1 建立一個獨立的 Migration

Migration 以檔案的形式存放在 db/migrate 目錄中,每一個 migration 類。檔名的形式是 YYYYMMDDHHMMSS_create_products.rb,即UTC時間戳 標識 migration 後跟下劃線後跟名稱 migration 的。 migration 類的名稱(CamelCased 版本) 應該匹配檔名的後半部分。例如 20080906120000_create_products.rb 應該定義類 CreateProducts20080906120001_add_details_to_products.rb 應該定義 AddDetailsToProducts。 Rails 使用這個時間戳來確定哪個 migration 應該以什麼順序執行,所以如果你從另一個複製 migration 應用程式或自己生成檔案,注意它在訂單中的位置。

當然,計算時間戳並不好玩,所以 Active Record 提供了一個 發電機為您處理製作:

$ bin/rails generate migration AddPartNumberToProducts

這將建立一個適當命名的空 migration:

class AddPartNumberToProducts < ActiveRecord::Migration[7.0]
  def change
  end
end

這個生成器可以做的不僅僅是在檔名上附加一個時間戳。 基於命名約定和附加(可選)引數,它可以 也開始充實 migration。

如果 migration 名稱的格式為“AddColumnToTable”或 “RemoveColumnFromTable”,後跟列名列表和 然後鍵入一個 migration 包含適當的 add_columnremove_column 語句將被建立。

$ bin/rails generate migration AddPartNumberToProducts part_number:string

會產生

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

如果您想在新列上新增索引,您也可以這樣做。

$ bin/rails generate migration AddPartNumberToProducts part_number:string:index

將生成適當的 add_columnadd_index 語句:

class AddPartNumberToProducts < ActiveRecord::Migration[7.0]
  def change
    add_column :products, :part_number, :string
    add_index :products, :part_number
  end
end

同樣,您可以生成一個 migration 來從命令列中刪除一列:

$ bin/rails generate migration RemovePartNumberFromProducts part_number:string

產生

class RemovePartNumberFromProducts < ActiveRecord::Migration[7.0]
  def change
    remove_column :products, :part_number, :string
  end
end

您不僅限於一個神奇生成的列。例如:

$ bin/rails generate migration AddDetailsToProducts part_number:string price:decimal

產生

class AddDetailsToProducts < ActiveRecord::Migration[7.0]
  def change
    add_column :products, :part_number, :string
    add_column :products, :price, :decimal
  end
end

如果 migration 名稱的格式為“CreateXXX”並且是 後跟列名和型別列表,然後是 migration 建立表 將生成帶有列出的列的 XXX。例如:

$ bin/rails generate migration CreateProducts name:string part_number:string

產生

class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :name
      t.string :part_number

      t.timestamps
    end
  end
end

與往常一樣,為您生成的內容只是一個起點。你可以加 或透過編輯 db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb 檔案。

此外,生成器接受列型別為 references(也可作為 belongs_to)。例如,

$ bin/rails generate migration AddUserRefToProducts user:references

生成以下 add_reference 呼叫:

class AddUserRefToProducts < ActiveRecord::Migration[7.0]
  def change
    add_reference :products, :user, foreign_key: true
  end
end

這個 migration 將建立一個 user_id 列,references 是一個 建立列、索引、外部 keys 甚至多型的簡寫 association 列。

如果 JoinTable 是名稱的一部分,還有一個生成器將生成連線表:

$ bin/rails generate migration CreateJoinTableCustomerProduct customer product

將產生以下 migration:

class CreateJoinTableCustomerProduct < ActiveRecord::Migration[7.0]
  def change
    create_join_table :customers, :products do |t|
      # t.index [:customer_id, :product_id]
      # t.index [:product_id, :customer_id]
    end
  end
end

2.2 Model 發電機

model 和 scaffold 生成器將建立適合新增的 migrations 一個新的 model。此 migration 已包含建立 相關表。如果你告訴 Rails 你想要什麼列,那麼語句 新增這些列也將被建立。例如,執行:

$ bin/rails generate model Product name:string description:text

將建立一個看起來像這樣的 migration

class CreateProducts < ActiveRecord::Migration[7.0]
  def change
    create_table :products do |t|
      t.string :name
      t.text :description

      t.timestamps
    end
  end
end

您可以根據需要附加任意數量的列名/型別對。

2.3 傳遞修飾符

一些常用的型別修飾符可以直接傳入 命令列。它們用花括號括起來並遵循欄位型別:

例如,執行:

$ bin/rails generate migration AddDetailsToProducts 'price:decimal{5,2}' supplier:references{polymorphic}

將產生一個看起來像這樣的 migration

class AddDetailsToProducts < ActiveRecord::Migration[7.0]
  def change
    add_column :products, :price, :decimal, precision: 5, scale: 2
    add_reference :products, :supplier, polymorphic: true
  end
end

提示:檢視生成器幫助輸出以獲取更多詳細資訊。

3 寫一個 Migration

使用其中一個生成器建立 migration 後,是時候 開始工作!

3.1 建立表

create_table 方法是最基本的方法之一,但大多數時候, 將使用 model 或 scaffold 生成器為您生成。一個典型的 使用將是

create_table :products do |t|
  t.string :name
end

它建立了一個 products 表,其中包含一個名為 name 的列。

預設情況下,create_table 將建立一個名為 id 的主 key。你可以改變 帶有 :primary_key 選項的主 key 的名稱(不要忘記 更新相應的 model),或者,如果您根本不需要主要的 key,您 可以透過選項 id: false。如果您需要傳遞特定於資料庫的選項 您可以在 :options 選項中放置一個 SQL 片段。例如:

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

ENGINE=BLACKHOLE 附加到用於建立表的 SQL 語句。

可以在 create_table 塊內建立的列上建立索引 透過將 true 或選項雜湊傳遞給 :index 選項:

create_table :users do |t|
  t.string :name, index: true
  t.string :email, index: { unique: true, name: 'unique_emails' }
end

您也可以傳遞帶有表的任何描述的 :comment 選項 將儲存在資料庫本身中,並且可以透過資料庫管理進行 viewed 工具,例如 MySQL Workbench 或 PgAdmin III。強烈建議指定 migrations 中對大型資料庫應用程式的評論,因為它可以幫助人們 瞭解資料 model 並生成文件。 目前只有 MySQL 和 PostgreSQL 介面卡支援註釋。

3.2 建立連線表

migration 方法 create_join_table 建立一個 HABTM(擁有並屬於 許多)連線表。一個典型的用途是:

create_join_table :products, :categories

它建立了一個 categories_products 表,其中有兩列稱為 category_idproduct_id。這些列的選項 :null 設定為 預設為 false。這可以透過指定 :column_options 來覆蓋 選項:

create_join_table :products, :categories, column_options: { null: true }

預設情況下,連線表的名稱來自前兩個的並集 按字母順序提供給 create_join_table 的引數。 要自定義表的名稱,請提供 :table_name 選項:

create_join_table :products, :categories, table_name: :categorization

建立一個 categorization 表。

create_join_table 也接受一個塊,你可以用它來新增索引 (預設情況下不建立)或附加列:

create_join_table :products, :categories do |t|
  t.index :product_id
  t.index :category_id
end

3.3 換表

create_table 的近親是 change_table,用於改變現有的 表。它的使用方式與 create_table 類似,但物件 屈服於塊知道更多的技巧。例如:

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

刪除 descriptionname 列,建立一個 part_number 字串 列並在其上新增索引。最後,它重新命名了 upccode 列。

3.4 更改列

remove_columnadd_column Rails 提供了 change_column migration 方法。

change_column :products, :part_number, :text

這會將 products 表上的列 part_number 更改為 :text 欄位。 注意 change_column 命令是不可逆的。

除了change_columnchange_column_nullchange_column_default 方法專門用於更改非空約束和預設值 列的 values。

change_column_null :products, :name, false
change_column_default :products, :approved, from: true, to: false

這將產品上的 :name 欄位設定為 NOT NULL 列和預設值 :approved 欄位的 value 從真到假。

你也可以把上面的 change_column_default migration 寫成 change_column_default :products, :approved, false,但與之前不同 例如,這將使您的 migration 不可逆。

3.5 列修飾符

建立或更改列時可以應用列修飾符:

  • comment 為列添加註釋。
  • collation 指定 stringtext 列的排序規則。
  • default 允許在列上設定預設的 value。請注意,如果您 正在使用動態 value(例如日期),預設只會被計算 第一次(即在應用 migration 的日期)。將 nil 用於 NULL
  • limit 設定 string 列的最大字元數 以及 text/binary/integer 列的最大位元組數。
  • null 在列中允許或禁止 NULL values。
  • precision 指定 decimal/numeric/datetime/time 列的精度。
  • scale 指定 decimalnumeric 列的比例, 表示小數點後的位數。

對於 add_columnchange_column,沒有新增索引的選項。 它們需要使用 add_index 單獨新增。

某些介面卡可能支援其他選項;檢視介面卡特定的 API 文件 瞭解更多資訊。

nulldefault 不能透過命令列指定。

3.6 參考資料

add_reference 方法允許建立一個適當命名的列。

add_reference :users, :role

這個 migration 將在使用者表中建立一個 role_id 列。它創造了一個 此列的索引也是如此,除非明確告知不要使用 index: false 選項:

add_reference :users, :role, index: false

方法 add_belongs_toadd_reference 的別名。

add_belongs_to :taggings, :taggable, polymorphic: true

多型選項將在標籤表上建立兩列,它們可以 用於多型 associations:taggable_typetaggable_id

可以使用 foreign_key 選項建立外部 key。

add_reference :users, :role, foreign_key: true

如需更多 add_reference 選項,請訪問 API 文件

也可以刪除引用:

remove_reference :products, :user, foreign_key: true, index: false

3.7 國外 Keys

雖然不是必需的,但您可能希望將外部 key 約束新增到 保證參照完整性

add_foreign_key :articles, :authors

這在 articlesauthor_id 列中添加了一個新的外部 key 桌子。 key 引用 authors 表的 id 列。如果 列名不能從表名派生,您可以使用 :column:primary_key 選項。

Rails 會為每個外來的 key 生成一個名字 fk_rails_ 後跟 10 個確定性的字元 從 from_tablecolumn 生成。 如果需要,可以使用 :name 選項指定不同的名稱。

Active Record 僅支援單列外部 keys。 executestructure.sql 需要使用複合國外 keys。看 模式轉儲和你

國外的 keys 也可以去掉:

# 讓 Active Record 找出列名
remove_foreign_key :accounts, :branches

# 刪除特定列的外部 key
remove_foreign_key :accounts, column: :owner_id

# 按名稱刪除外部 key
remove_foreign_key :accounts, name: :special_fk_name

3.8 當 Helper 不夠用時

如果 Active Record 提供的 helpers 不夠用,可以使用 execute 執行任意SQL的方法:

Product.connection.execute("UPDATE products SET price = 'free' WHERE 1=1")

有關各個方法的更多詳細資訊和示例,請檢視 API 文件。 特別是文件 ActiveRecord::ConnectionAdapters::SchemaStatements (它提供了 changeupdown 方法中可用的方法), ActiveRecord::ConnectionAdapters::TableDefinition (它提供了 create_table 生成的物件上可用的方法) 和 ActiveRecord::ConnectionAdapters::Table (它提供了 change_table 生成的物件上可用的方法)。

3.9 使用 change 方法

change 方法是編寫 migrations 的主要方式。它適用於 大多數情況下,Active Record 知道如何反轉 migration 自動地。目前,change方法只支援這些migration 定義:

change_table也是可逆的,只要block不呼叫changechange_defaultremove

如果您提供列型別作為第三個,則 remove_column 是可逆的 爭論。也提供原始列選項,否則 Rails 不能 回滾時完全重新建立列:

remove_column :posts, :slug, :string, null: false, default: ''

如果您需要使用任何其他方法,則應使用 reversible 或者編寫 updown 方法而不是使用 change 方法。

3.10 使用 reversible

複雜的 migrations 可能需要處理 Active Record 不知道如何處理 扭轉。您可以使用 reversible 指定執行 migration 以及恢復它時還需要做什麼。例如:

class ExampleMigration < ActiveRecord::Migration[7.0]
  def change
    create_table :distributors do |t|
      t.string :zipcode
    end

    reversible do |dir|
      dir.up do
        # add a CHECK constraint
        execute <<-SQL
          ALTER TABLE distributors
            ADD CONSTRAINT zipchk
              CHECK (char_length(zipcode) = 5) NO INHERIT;
        SQL
      end
      dir.down do
        execute <<-SQL
          ALTER TABLE distributors
            DROP CONSTRAINT zipchk
        SQL
      end
    end

    add_column :users, :home_page_url, :string
    rename_column :users, :email, :email_address
  end
end

使用 reversible 將確保指令在 順序也對。如果還原前面的示例 migration, down 塊將在移除 home_page_url 列後執行,並且 就在表 distributors 被刪除之前。

有時您的 migration 會做一些完全不可逆的事情;為了 例如,它可能會破壞一些資料。在這種情況下,您可以提高 ActiveRecord::IrreversibleMigration 在您的 down 塊中。如果有人嘗試 要恢復您的 migration,將顯示一條錯誤訊息,指出它 做不到。

3.11 使用 up/down 方法

您也可以使用 updown 方法使用舊樣式的 migration 而不是 change 方法。 up 方法應該描述你想對你的 模式,你的 migration 的 down 方法應該恢復 由 up 方法完成的轉​​換。換句話說,資料庫模式 如果您先執行 up,然後執行 down,則應該保持不變。例如,如果你 在 up 方法中建立一個表,您應該在 down 方法中刪除它。它 以完全相反的順序執行轉換是明智的 在 up 方法中製作。 reversible 部分中的示例等效於:

class ExampleMigration < ActiveRecord::Migration[7.0]
  def up
    create_table :distributors do |t|
      t.string :zipcode
    end

    # add a CHECK constraint
    execute <<-SQL
      ALTER TABLE distributors
        ADD CONSTRAINT zipchk
        CHECK (char_length(zipcode) = 5);
    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 distributors
        DROP CONSTRAINT zipchk
    SQL

    drop_table :distributors
  end
end

如果你的 migration 是不可逆的,你應該加註 ActiveRecord::IrreversibleMigration 來自您的 down 方法。如果有人嘗試 要恢復您的 migration,將顯示一條錯誤訊息,指出它 做不到。

3.12 恢復以前的 Migration

您可以使用 Active Record 的 revert 方法回滾 migrations 的能力:

require_relative "20121212123456_example_migration"

class FixupExampleMigration < ActiveRecord::Migration[7.0]
  def change
    revert ExampleMigration

    create_table(:apples) do |t|
      t.string :variety
    end
  end
end

revert 方法也接受要反轉的指令塊。 這對於恢復以前的 migrations 的選定部分可能很有用。 例如,假設 ExampleMigration 已提交併且它 後來決定最好使用 Active Record 驗證, 代替 CHECK 約束,以驗證郵政編碼。

class DontUseConstraintForZipcodeValidationMigration < ActiveRecord::Migration[7.0]
  def change
    revert do
      # copy-pasted code from ExampleMigration
      reversible do |dir|
        dir.up do
          # add a CHECK constraint
          execute <<-SQL
            ALTER TABLE distributors
              ADD CONSTRAINT zipchk
                CHECK (char_length(zipcode) = 5);
          SQL
        end
        dir.down do
          execute <<-SQL
            ALTER TABLE distributors
              DROP CONSTRAINT zipchk
          SQL
        end
      end

      # The rest of the migration was ok
    end
  end
end

不使用 revert 也可以編寫相同的 migration 但這將涉及更多步驟:顛倒順序 create_tablereversible 的,替換 create_tabledrop_table,最後由 down 替換 up,反之亦然。 這一切都由 revert 處理。

4 執行 Migrations

Rails 提供了一組 rails 命令來執行某些 migrations 組。

您將使用的第一個 migration 相關 rails 命令可能是 bin/rails db:migrate。在最基本的形式中,它只執行 changeup 所有尚未執行的 migrations 的方法。如果有 沒有這樣的migrations,它退出。它將按順序執行這些 migrations 在 migration 日期。

注意,執行 db:migrate 命令也會呼叫 db:schema:dump 命令,它 將更新您的 db/schema.rb 檔案以匹配您的資料庫結構。

如果指定目標版本,Active Record 將執行所需的 migrations (change, up, down) 直到達到指定的版本。版本 是 migration 檔名的數字字首。例如,要遷移 到版本 20080906120000 執行:

$ bin/rails db:migrate VERSION=20080906120000

如果版本 20080906120000 大於當前版本(即 向上遷移),這將執行 change(或 up)方法 在所有 migrations 上直到和 包括20080906120000,以後不會再執行migrations。如果 向下遷移,這將在所有 migrations 上執行 down 方法 低至但不包括 20080906120000。

4.1 回滾

一個常見的任務是回滾最後一個 migration。例如,如果你做了一個 錯誤並希望改正。而不是追蹤版本 與之前的 migration 相關聯的編號,您可以執行:

$ bin/rails db:rollback

這將透過恢復 change 來回滾最新的 migration 方法或透過執行 down 方法。如果您需要撤消 幾個migrations你可以提供一個STEP引數:

$ bin/rails db:rollback STEP=3

將恢復最後 3 migrations。

db:migrate:redo 命令是回滾然後遷移的快捷方式 再次備份。與 db:rollback 命令一樣,您可以使用 STEP 引數 如果您需要返回多個版本,例如:

$ bin/rails db:migrate:redo STEP=3

這些 rails 命令都沒有做任何你用 db:migrate 做不到的事情。他們 是為了方便起見,因為您不需要明確指定 要遷移到的版本。

4.2 設定資料庫

bin/rails db:setup 命令將建立資料庫、載入模式並初始化 它與種子資料。

4.3 重置資料庫

bin/rails db:reset 命令將刪除資料庫並重新設定它。這是 功能上等同於 bin/rails db:drop db:setup

這與執行所有 migrations 不同。它只會使用 當前 db/schema.rbdb/structure.sql 檔案的內容。如果一個 migration 不能回滾, bin/rails db:reset 可能幫不了你。要了解有關轉儲架構的更多資訊,請參閱 Schema Dumping and You 部分。

4.4 執行特定的 Migrations

如果您需要向上或向下執行特定的 migration,則 db:migrate:updb:migrate:down 命令會做到這一點。只需指定適當的版本和 相應的 migration 將有其 changeupdown 方法 呼叫,例如:

$ bin/rails db:migrate:up VERSION=20080906120000

將透過執行 change 方法(或 up 方法)。該命令將 首先檢查migration是否已經執行,如果 Active Record 認為它已經運行了。

4.5 在不同環境下執行 Migrations

預設情況下,執行 bin/rails db:migrate 將在 development 環境中執行。 要針對另一個環境執行 migrations,您可以使用 RAILS_ENV 執行命令時的環境變數。例如執行 migrations 針對您可以執行的 test 環境:

$ bin/rails db:migrate RAILS_ENV=test

4.6 改變執行 Migrations 的輸出

預設情況下,migrations 會準確地告訴您他們在做什麼以及花了多長時間。 A migration 建立表並新增索引可能會產生這樣的輸出

==  CreateProducts: migrating =================================================
-- create_table(:products)
   -> 0.0028s
==  CreateProducts: migrated (0.0028s) ========================================

migrations 中提供了幾種方法,允許您控制所有這些:

方法 目的
suppress_messages 將一個塊作為引數並抑制該塊生成的任何輸出。
say 接受一個訊息引數並按原樣輸出它。可以傳遞第二個布林引數來指定是否縮排。
say_with_time 輸出文字以及執行其塊所需的時間。如果塊返回一個整數,則假定它是受影響的行數。

例如,這個 migration:

class CreateProducts < ActiveRecord::Migration[7.0]
  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

生成以下輸出

==  CreateProducts: migrating =================================================
-- Created a table
   -> and an index!
-- Waiting for a while
   -> 10.0013s
   -> 250 rows
==  CreateProducts: migrated (10.0054s) =======================================

如果你想讓 Active Record 不輸出任何東西,那麼執行 bin/rails db:migrate VERBOSE=false 將抑制所有輸出。

5 更改現有的 Migrations

偶爾你會在寫 migration 時出錯。如果你有 已經運行了 migration,那麼你不能只編輯 migration 並執行 migration 再次:Rails 認為它​​已經運行了 migration,所以會這樣做 執行 bin/rails db:migrate 時什麼也沒有。您必須回滾 migration(對於 以 bin/rails db:rollback 為例),編輯你的 migration,然後執行 bin/rails db:migrate 執行更正後的版本。

一般來說,編輯現有的 migrations 不是一個好主意。你會 為你自己和你的同事創造額外的工作,並導致嚴重的頭痛 如果現有版本的 migration 已經在生產環境中執行 機器。相反,您應該編寫一個新的 migration 來執行更改 你需要。編輯一個新生成的 migration 還沒有 致力於原始碼控制(或更一般地說,尚未傳播 超出您的開發機器)相對無害。

revert 方法在編寫新的 migration 以撤消時很有用 以前的 migrations 全部或部分 (參見上面的 Reverting Previous Migrations)。

6 模式傾銷和你

6.1 架構檔案有什麼用?

Migrations,儘管它們可能很強大,但並不是您的權威來源 資料庫架構。您的資料庫仍然是權威來源。預設情況下, Rails 生成 db/schema.rb 試圖捕獲當前狀態 您的資料庫架構。

建立您的新實例往往會更快並且更不容易出錯 透過 bin/rails db:schema:load 載入模式檔案來載入應用程式的資料庫 而不是重放整個 migration 歷史。 舊 migrations 可能無法正確應用,如果 migrations 使用不斷變化的外部依賴項或依賴應用程式程式碼 與您的 migrations 分開進化。

如果您想快速檢視什麼屬性,架構檔案也很有用 Active Record 物件有。此資訊不在 model 的程式碼中,而是在 經常分佈在幾個migrations,但資訊很好 總結在模式檔案中。

6.2 模式轉儲的型別

Rails 生成的模式轉儲的格式由 config/application.rb 中的 config.active_record.schema_format 設定。經過 預設格式為 :ruby,但也可以設定為 :sql

如果選擇 :ruby,則架構儲存在 db/schema.rb 中。如果你看 在這個檔案中,你會發現它看起來非常像一個非常大的 migration:

ActiveRecord::Schema.define(version: 2008_09_06_171750) 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

在許多方面,這正是事實。該檔案是透過檢查 資料庫並使用create_tableadd_index等表示其結構 在。

db/schema.rb 無法表達您的資料庫可能支援的所有內容,例如 觸發器、序列、儲存過程等。 而 migrations 可以使用 execute 建立不受支援的資料庫結構 Ruby migration DSL,這些構造可能無法由 模式轉儲器。如果您正在使用這些功能,您應該設定架構 格式為 :sql 以獲得準確的模式檔案 建立新的資料庫實例。

當schema格式設定為:sql時,會dump資料庫結構 使用特定於資料庫的工具進入 db/structure.sql。例如,對於 PostgreSQL,使用了 pg_dump 實用程式。對於 MySQL 和 MariaDB,此檔案將 包含各種表的 SHOW CREATE TABLE 的輸出。

要從 db/structure.sql 載入架構,請執行 bin/rails db:schema:load。 載入這個檔案是透過執行它包含的 SQL 語句來完成的。經過 定義,這將建立資料庫結構的完美副本。

6.3 模式轉儲和原始碼控制

因為模式檔案通常用於建立新的資料庫,所以它強烈 建議您將架構檔案簽入原始碼管理。

當兩個分支修改架構時,架構檔案中可能會發生合併衝突。 要解決這些衝突,請執行 bin/rails db:migrate 以重新生成架構檔案。

7 Active Record 和參照完整性

Active Record 方式聲稱智慧屬於你的 models,而不是在 資料庫。因此,觸發器或約束等功能, 將一些情報推回到資料庫中,並不是很嚴重 用過的。

諸如 validates :foreign_key, uniqueness: true 之類的驗證是一種方式 其中 models 可以強制執行資料完整性。 :dependent 選項在 associations 允許 models 自動銷燬子物件時 父級被銷燬。就像在應用程式級別執行的任何東西一樣, 這些不能保證參照完整性,所以有些人會增加它們 資料庫中有外來鍵約束

雖然 Active Record 沒有提供直接使用的所有工具 這樣的特性,可以使用execute方法來執行任意SQL。

8 Migrations 和種子資料

Rails 的 migration 特性的主要目的是發出修改 模式使用一致的過程。 Migrations 也可以使用 新增或修改資料。這在無法銷燬的現有資料庫中很有用 並重新建立,例如生產資料庫。

class AddInitialProducts < ActiveRecord::Migration[7.0]
  def up
    5.times do |i|
      Product.create(name: "Product ##{i}", description: "A product.")
    end
  end

  def down
    Product.delete_all
  end
end

為了在建立資料庫後新增初始資料,Rails 有一個內建的 “種子”功能可加快程序。這是特別 在開發和測試環境中頻繁重新載入資料庫時很有用。 要開始使用此功能,請填寫 db/seeds.rb 一些 Ruby 程式碼,並執行 bin/rails db:seed

5.times do |i|
  Product.create(name: "Product ##{i}", description: "A product.")
end

這通常是設定空白資料庫的更簡潔的方法 應用。

9 舊的 Migrations

db/schema.rbdb/structure.sql 是您當前狀態的快照 資料庫,並且是重建該資料庫的權威來源。這 可以刪除舊的 migration 檔案。

當您刪除 db/migrate/ 目錄中的 migration 檔案時,任何環境 當這些檔案仍然存在時執行 bin/rails db:migrate 的位置將包含一個引用 到特定於它們在內部 Rails 資料庫中的 migration 時間戳 名為 schema_migrations 的表。此表用於跟蹤是否 migrations 已在特定環境中執行。

如果您執行 bin/rails db:migrate:status 命令,它會顯示狀態 (向上或向下)每個 migration,你應該看到 ********** NO FILE ********** 顯示在任何已刪除的 migration 檔案旁邊,該檔案曾經在 特定環境,但在 db/migrate/ 目錄中已找不到。

不過,有一個警告。從引擎安裝遷移的 Rake 任務是冪等的。由於先前安裝而存在於父應用程式中的 Migration 將被跳過,並使用新的前導時間戳複製丟失的那些。如果您刪除舊引擎遷移並再次執行安裝任務,您將獲得帶有新時間戳的新檔案,並且 db:migrate 將嘗試再次執行它們。

因此,您通常希望保留來自引擎的 migrations。他們有這樣的特殊評論:

# 這個migration來自blorgh(原為20210621082949)

回饋

我們鼓勵您幫助提高本指南的品質。

如果您發現任何拼寫錯誤或資訊錯誤,請提供回饋。 要開始回饋,您可以閱讀我們的 回饋 部分。

您還可能會發現不完整的內容或不是最新的內容。 請務必為 main 新增任何遺漏的文件。假設是 非穩定版指南(edge guides) 請先驗證問題是否已經在主分支上解決。 請前往 Ruby on Rails 指南寫作準則 查看寫作風格和慣例。

如果由於某種原因您發現要修復的內容但無法自行修補,請您 提出 issue

關於 Ruby on Rails 的任何類型的討論歡迎提供任何文件至 rubyonrails-docs 討論區