Francis's Octopress Blog

A blogging framework for hackers.

Guides Rubyonrails Migrations (en and CN)

guides rubyonrails Migrations (EN and CN)

Migrations

Migrations are a convenient way for you to alter移动your database in a structured and organized manner.Migrations是一种很便捷的方法让你能够以一种结构化的和有组织的方式来迁移你的数据库。You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them.你可以手动编辑SQL片段,而且你有责任把这些告诉其他的开发人员,因为他们需要开发和使用它们。You’d also have to keep track of which changes need to be run against the production machines next time you deploy.你也可以跟踪对你部署的代码在接下来的production机器(将会)发生的变化。

Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate.Active Record跟踪并迁移你已经运行过的(代码和数据),而你只需要在更新了你的源代码的时候执行rake db:migrateActive Record will work out which migrations should be run.Active Recor将会计算出那些迁移需要被执行。It will also update your db/schema.rb file to match the structure of your database.它还会更新你的db/schema.rb文件使其于你的数据库结构相匹配。

Migrations also allow you to describe these transformations using Ruby.Migrations同样允许你使用Ruby来描述这些转换。The great thing about this is that (like most of Active Records functionality) it is database independent: you dont need to worry about the precise syntax of CREATE TABLE any more than you worry about variations on SELECT (you can drop down to raw SQL for database specific features).值得高兴的事情是(就像大多数的Active Record’s工厂)它是与数据独立的:你再也不需要担心准确的语法来CREATE TABLE也不需要担心SELECT 的变化。For example you could use SQLite3 in development, but MySQL in production.例如你可以使用SQLite3开发,但是在发布的产品中使用MySQL

Youll learn all about migrations including:下面你将了解到的migrations包括:

  • The generators you can use to create them 你可以使用generators来创建他们(数据库表单)
  • The methods Active Record provides to manipulate操纵your database Active Record提供方法来操纵你的数据库
  • The Rake tasks that manipulate them 使用Rake命令操作这些(迁移)
  • How they relate to schema.rb 它们是如何映射到schema.rb

1 Anatomy of a Migration

Before we dive into the details of a migration, here are a few examples of the sorts of things you can do:在深入migration的详细介绍之前,下面有一系列的例子你可以尝试一下:

class CreateProducts < ActiveRecord::Migration

def up

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

 

def down

drop_table :products

end

end

This migration adds a table called products with a string column called name and a text column called description.这次migration添加了一个名叫products的表它有一个叫name的字符串的列和一个叫description的文本框的列。A primary key column called id will also be added, however since this is the default we do not need to ask for this.一个名叫id的主键列也被添加,然而因为这是默认操作的不需要我们刻意添加。The timestamp columns created_at and updated_at which Active Record populates automatically will also be added.还添加了timestamp字段,products表单会在created_atupdated_at的时候通过Active Record自动填充timestamp字段。Reversing this migration is as simple as dropping the table.撤销这次migration就像dropping这个表。

 

Migrations are not limited to changing the schema.Migrations不限制更改schemaYou can also use them to fix bad data in the database or populate new fields:你可以使用(schema)它们来修复坏的数据或者添加新的字段:

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

Some caveats apply to using models in your migrations.一些在modelmigrations中的注意事项

This migration adds a receive_newsletter column to the users table.(上面)这个migration添加一个receive_newsletter字段到user表。We want it to default to false for new users, but existing users are considered to have already opted in, so we use the User model to set the flag to true for existing users.我们希望对于新用户默认设置receive_newsletter字段为fasle,但是存在的用户被认为已经有(自己的)选择,因此我们通过在User model中存在的用户设置为True的标识(来保留以后信息)。

Rails 3.1 makes migrations smarter by providing a new change method.Rails3.1通过提供一个新的change方法,使得migrations更加智能化。This method is preferred首选for writing constructive migrations (adding columns or tables).这个方法是用来做(数据库)结构迁移(添加或删除字段)的首选。The migration knows how to migrate your database and reverse it when the migration is rolled back without the need to write a separate down method.migration知道怎样迁移你的数据库以及不需要单独的编写down方法来处理回滚是的migration

class CreateProducts < ActiveRecord::Migration

def change

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

end

1.1 Migrations are Classes Migrations是一个类

A migration is a subclass of ActiveRecord::Migration that implements two methods: up (perform the required transformations) and down (revert them).一个migration类是ActiveRecord::Migration的子类,它实现了两个方法:up(执行所请求的转换)和down(撤销所做的更改)。

Active Record provides methods that perform common data definition tasks in a database independent way (you’ll read about them in detail later):Active Record提供了了在数据库中执行常见数据定义的方法(你将会在后面看到详细的介绍)。

  • create_table
  • change_table
  • drop_table
  • add_column
  • change_column
  • rename_column
  • remove_column
  • add_index
  • remove_index

If you need to perform tasks specific to your database (for example create a foreignkey constraint约束) then the execute function allows you to execute arbitrary SQL.如果你在你的数据库中需要处理特殊的任务(例如创建一个foreign key约束)那么execute功能允许你执行任意的SQL(语句)。A migration is just a regular Ruby class so you’re not limited to these functions. migration仅仅是一个Ruby类,因此你不必仅仅局限于现有的这些功能。For example after adding a column you could write code to set the value of that column for existing records (if necessary using your models).例如在添加了一个字段之后你可以添加代码来设置这个字段在存在记录中的值(如果在你的model中需要)。

On databases that support transactions处理办理with statements that change the schema (such as PostgreSQL or SQLite3), migrations are wrapped in a transaction.当数据库支持通过声明来改变数据库的结构(例如PostgreSQLSQLite3),会在包含在migration处理中(直接改变数据库的结构)。If the database does not support this (for example MySQL) then when a migration fails the parts of it that succeeded will not be rolled back. 如果数据库不支持这样的功能(比如MySQL)然后migration会有部分失败——成功添加的(数据)将不会回滚。You will have to unpick the changes that were made by hand.你必须手动的分开这些改变。

1.2 What’s in a Name (数据库文件)名称中的信息

Migrations are stored in files in db/migrate, one for each migration class.Mgirations以单个文件的形式被存放在文件夹db/migrateThe name of the file is of the form YYYYMMDDHHMMSS_create_products.rb, that is to say a UTC timestamp时间戳identifying确定the migration followed by an underscore下划线followed by the name of the migration.migration)文件是以form命名的,还看见了一个在migration完成之时的时间戳接着是下划线接着是migration的名字。The name of the migration class (CamelCased version) should match the latter part of the file name. migraiton类(使用驼峰命名法)的名字应该和migration文件的名称的最后部分相匹配。For example 20080906120000_create_products.rb should define CreateProducts and 20080906120001_add_details_to_products.rb should define AddDetailsToProducts. If you do feel the need to change the file name then you have to update the name of the class inside or Rails will complain about a missing class.如果你觉得需要改变migration文件的名字你必须同样修改文件里边migration类的名字,不然Rails会找不到migration类。

Internally Rails only uses the migration’s number (the timestamp) to identify them.Rails内部只使用migration编号(时间戳)来确定他们。Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated.Rails2.1之前migration编号从1开始然后在每次migration被创建过后增加。With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them.随着开发人员的增多这样会使的很容易产生冲突这就需要你回滚migrations和重新编号他们。developers With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them.Rails2.1的开发人员通过migraiton文件的创建时间指明每个文件在很大程度上避免了冲突(的发生)。You can revert to the old numbering scheme by adding the following line to config/application.rb.你可以还原带旧的版本通过在config/application.rb文件中添加如下行:

config.active_record.timestamped_migrations = false

The combination组合of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.时间戳和migrations的名字的组合使得Rails可以处理多个开发人员的普遍情况。

For example Alice adds migrations 20080906120000 and 20080906123000 and Bob adds 20080906124500 and runs it.比如Alice 添加了migration 20080906120000 and 20080906123000以及Bob添加并运行了20080906124500Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Alice完成了他的更改并提交在他的migrationss中,并且Bob pull down了最新的更改。Rails knows that it has not run Alices two migrations so rake db:migrate would run them (even though Bobs migration with a later timestamp has been run), and similarly migrating down would not run their down methods.

Of course this is no substitution for communication within the team.当然这些在团队交流中是不可避免的。For example, if Alices migration removed a table that Bobs migration assumed to exist, then trouble would certainly strike.例如如果Alicemigration中移除了一个表但是Bob’smigraion假设它还在,那么麻烦就来了。

1.3 Changing Migrations

Occasionally you will make a mistake when writing a migration.偶尔你在写入一个migration的时候犯个错误。If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run rake db:migrate.如果你已经运行了migration接着你不可能再去编辑和运行这个(错误的)migraionRails认为(你已经)运行了migration因此在你运rake db:migrate的时候不会做任何改变。You must rollback the migration (for example with rake db:rollback), edit your migration and then run rake db:migrate to run the corrected version.你必须回滚migation(例如rake db:rollback),编辑修改你的migraion然后运行正确的版本rake db:migrate

In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines.在一般情况下,编辑存在的migrations不是一个好主意:因为这样你会给你自己或者你的合作成员产生额外的工作,头疼的原因是如果存在的migration已经在production机器中运行。Instead you should write a new migration that performs the changes you require.作为替代你应该编写一个新的migration来执行你需要的更改。Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless.编辑一个刚生成的还没有提交到软代码控制migration(或者更一般的情况还没有传播出你的开发机器)相对危害较轻。

1.4 Supported Types支持的类型

Active Record supports the following types:Active Record支持如下类型:

  • :primary_key
  • :string
  • :text
  • :integer
  • :float
  • :decimal 10进制
  • :datetime
  • :timestamp
  • :time
  • :date
  • :binary
  • :boolean

These will be mapped onto an appropriate underlying底层database type, for example with MySQL :string is mapped to VARCHAR(255).这些将会被映射为合适底层数据库的类型,例如使用MySQL :string类型将会映射成VARCHAR(255)You can create columns of types not supported by Active Record when using the non-sexy syntax, for example你可以在创建Active Record不支持的字段,使用non-sexy语法,例如

create_table :products do |t|

t.column :name, ‘polygon’, :null => false

end

This may however hinder阻碍portability移植to other databases.不过这可能会阻碍移植到其它数据库。

2 Creating a Migration新建一个Migrateion

2.1 Creating a Model新建一个Model

The model and scaffold generators will create migrations appropriate for adding a new model.modelgenerators创建器会为添加的新的model创建合适的migrationsThis migration will already contain instructions for creating the relevant有关table.这个migration已经包含在有关创建的的表的说明中。If you tell Rails what columns you want then statements声明for adding those will also be created. For example, running如果你告诉Rails将你随后声明的字段添加到创建migrations中,例如,运行

$ rails generate model Product name:string description:text

will create a migration that looks like this rails)将会新建一个像这样的migraion

class CreateProducts < ActiveRecord::Migration

def change

create_table :products do |t|

t.string :name

t.text :description

 

t.timestamps

end

end

end

You can append as many column name/type pairs as you want.你可以随意增加字段或者类型对。By default t.timestamps (which creates the updated_at and created_at columns that are automatically populated by Active Record) will be added for you.默认的t.timestampsActive Record 会自动生成updated_atcreated_at字段)会子添加在你的表中。

2.2 Creating a Standalone Migration新建一个独立的Migration

If you are creating migrations for other purposes (for example to add a column to an existing table) then you can use the migration generator:如果你正在因为其他的目的新建migrations(例如添加一个字段到一个存在的表)你可以使用migration创建器:

$ rails generate migration AddPartNumberToProducts

This will create an empty but appropriately named migration:这里会新建一个空的但是合适的migration

class AddPartNumberToProducts < ActiveRecord::Migration

def change

end

end

If the migration name is of the form “AddXXXToYYY” or “RemoveXXXFromYYY” and is followed by a list of column names and types then a migration containing the appropriate add_column and remove_column statements will be created.如果migration命名为AddXXXToYYY”RemoveXXXFromYYY”格式并且后跟有一些字段名称或类型,那么一个migration包含合适的add_columnremove_column将会被新建。

$ rails generate migration AddPartNumberToProducts part_number:string

ps:如果已经创建了一个AddPartNumberToProducts那么运行rails generate migration AddPartNumberToProducts part_number:string -f进行覆盖新建

will generate将会生成:

class AddPartNumberToProducts < ActiveRecord::Migration

def change

add_column :products, :part_number, :string

end

end

Similarly,同样,

$ rails generate migration RemovePartNumberFromProducts part_number:string

generates生成

class RemovePartNumberFromProducts < ActiveRecord::Migration

def up

remove_column :products, :part_number

end

 

def down

add_column :products, :part_number, :string

end

end

You are not limited to one magically神奇generated column, for example你不限制于一次只输入一个字段,例如

$ rails generate migration AddDetailsToProducts part_number:string price:decimal

generates生成

class AddDetailsToProducts < ActiveRecord::Migration

def change

add_column :products, :part_number, :string

add_column :products, :price, :decimal

end

end

As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit.一如往常,使用migration创建器生成的migration只是一个起点,你可以添加或删除其中的字段直到你满意为止。

 

The generated migration file for destructive migrations will still be old-style using the up and down methods.在创建的migration文件中对migrations进行破坏性的操作仍然使用老式的updown方法。This is because Rails doesnt know the original data types defined when you made the original changes.这是因为当你对初始migration做了更改,Rails不知道原始的数据类型定义。

 

3 Writing a Migration

Once you have created your migration using one of the generators it’s time to get to work!一旦你已经使用一种创建器创建了你的migration,现在是时候开始工作了。

3.1 Creating a Table创建一个表

Migration method create_table will be one of your workhorses. A typical use would be Migration方法create_table将会是一个你的驮马。下面是他的典型形式

create_table :products do |t|

t.string :name

end

which creates a products table with a column called name (and as discussed below, an implicit id column).上面的代码会创建一个products表,其中包含了一个name字段(和下面讨论的一样,一个隐藏的id字段)

The object yielded to the block allows you to create columns on the table.这个migration类产生代码块让你创建表中的字段。There are two ways of doing this: The first (traditional) form looks like有两种方法创建字段:首先(传统的)方式看起来像这样

create_table :products do |t|

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

end

the second form, the so called “sexy” migration, drops the somewhat redundant column method.第二种方式,也称为sexy” migration,丢掉了有些冗余的column方法。Instead, the string, integer, etc. methods create a column of that type.作为替代,如string,interger等等方法创建相应类型的字段。Subsequent parameters are the same.其后的参赛也在同一个字段中。

create_table :products do |t|

t.string :name, :null => false

end

HABTM, hasAndBelongsToMany 这是Active Recored功能里面比较复杂的一个东西。简单的来说,就是你有一个帖子,你给这个帖子定义了多个tag但是实际上tag也是独立的,一个tag会包含很多个不同的帖子。

By default create_table will create a primary key called id.通过默认的create_table将会创建一个叫做id的主键。You can change the name of the primary key with the :primary_key option (dont forget to update the corresponding相应的model) or if you dont want a primary key at all (for example for a HABTM join table) you can pass :id => false.你可以更改主键的名字通过使用:primary_key选项(不要忘记更新其相应的model)或者你根本不想要主键(例如添加一个HABTM关系到表中)你可以通过:id => falseIf you need to pass database specific options you can place an SQL fragment in the :options option. For example如果你需要数据库的特殊选项你可以放置一个SQL片段在:options选项中。例如

create_table :products, :options => “ENGINE=BLACKHOLE” do |t|

t.string :name, :null => false

end

will append ENGINE=BLACKHOLE to the SQL statement used to create the table (when using MySQL the default is ENGINE=InnoDB).将会添加ENGINE=BLACKHOLESQL声明中用于创建表单(当使用MySQL默认的(ENGINE)是InnoDB)。

3.2 Changing Tables更改表单

A close cousin of create_table is change_table, used for changing existing tables. create_table的一个近亲是change_table,用于更改存在的表单。It is used in a similar fashion to create_table but the object yielded to the block knows more tricks.它使用的是与create_table类似的方法,但是其类产生的块能够知道更多的技巧。(如移除字段,重命名字段)For example例如

change_table :products do |t|

t.remove :description, :name

t.string :part_number

t.index :part_number

t.rename :upccode, :upc_code

end

removes the description and name columns, creates a part_number column and adds an index on it. Finally it renames the upccode column.移除description字段和name字段,创建一个part_number字段并且添加一个index(索引)给part_number。最后它重命名了:upccodeThis is the same as doing下面的代码也可以达到同样的效果

remove_column :products, :description

remove_column :products, :name

add_column :products, :part_number, :string

add_index :products, :part_number

rename_column :products, :upccode, :upc_code

 

You dont have to keep repeating the table name and it groups all the statements related to modifying one particular table.你不必重复表单名称和组相关的所有声明,(而只需要重复)修改表的一部分。The individual个别transformation转换names are also shorter, for example remove_column becomes just remove and add_index becomes just index.个别的转换方法的名称也更加简短了,例如remove_column变成removeadd_index变成index##这里是说的新旧两种方法比较

3.3 Special Helpers个别的Helpers

Active Record provides some shortcuts for common functionality.Active Record提供了一些一般功能的快捷操作。It is for example very common to add both the created_at and updated_at columns and so there is a method that does exactly that:例如很常见的通过created_atupdated_at添加字段等等,这里是一个方法的简要例子

create_table :products do |t|

t.timestamps

end

will create a new products table with those two columns (plus the id column) whereas.将会新建一个products表单,有两个字段(加上id字段)。

change_table :products do |t|

t.timestamps

end

adds those columns to an existing table.添加一些字段到存在的表中。

 

The other helper is called references (also available as belongs_to). In its simplest form it just adds some readability另一个helper被称为references(也可用belongs_to)。在其最简单的形式,它仅仅增加一些可读性。

create_table :products do |t|

t.references :category

end

will create a category_id column of the appropriate type.将会创建一个适当形式的category_id字段。Note that you pass the model name, not the column name. Active Record adds the id for you. 注意那是你关联的model名称,不是字段名称。Active Record会给你添加idIf you have polymorphic多态性belongs_to associations then references will add both of the columns required:

create_table :products do |t|

t.references :attachment, :polymorphic => {:default => ‘Photo’}

end

will add an attachment_id column and a string attachment_type column with a default value ofPhoto.将会添加一个attachment_id 和一个默认值是Photo的字符串attachment_type字段。

The references helper does not actually create foreign key constraints for you. You will need to use execute for that or a plugin that adds foreignkeysupport.references helper没有真正创建外键约束给你。你需要使用execute或者一个plugin来添加外键支持。

If the helpers provided by Active Record arent enough you can use the execute function to execute arbitrary SQL.如果Active Record提供的helper不足以满足,你可以使用execute功能来执行任意SQL

For more details and examples of individual个别methods check the API documentation, in particular the documentation for ActiveRecord::ConnectionAdapters::SchemaStatements (which provides the methods available in the up and down methods), ActiveRecord::ConnectionAdapters::TableDefinition (which provides the methods available on the object yielded by create_table) and ActiveRecord::ConnectionAdapters::Table (which provides the methods available on the object yielded by change_table).

3.4 Writing Your change Method

The change method removes the need to write both up and down methods in those cases that Rails know how to revert the changes automatically. change(migration类方法)删除需要写进updown的方法,那样Rails知道怎样自动撤销更改。Currently, the change method supports only these migration definitions:目前,change方法支持如下的migration定义:

  • add_column
  • add_index
  • add_timestamps
  • create_table
  • remove_timestamps
  • rename_column
  • rename_index
  • rename_table

If you’re going to use other methods, you’ll have to write the up and down methods normally.如果你打算使用其他方法,你可以常规的写updown方法。

3.5 Writing Your down Method

The down method of your migration should revert the transformations done by the up method.迁移的down方法应该撤销up方法做的转换。In other words the database schema架构should be unchanged if you do an up followed by a down.换句话说数据库架构应该没有改变,如果你在up方法过后又执行down方法。For example if you create a table in the up method you should drop it in the down method. It is wise to do things in precisely the reverse order to in the up method.例如如果你创建使用up方法一个表你应该在down方法中去除它。For example

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 “ALTER TABLE products DROP FOREIGN KEY fk_products_categories”

drop_table :products

end

end

Sometimes your migration will do something which is just plain irreversible不可逆转的, for example it might destroy some data.有时候你的migration将会做一些不可逆转的事情。例如它可能会删除一些数据。In cases like those when you can’t reverse the migration you can raise ActiveRecord::IrreversibleMigration from your down method.在这样的情况中你不能撤销migration你可以在你的down方法中raise ActiveRecord::IrreversibleMigrationIf someone tries to revert your migration an error message will be displayed saying that it can’t be done.如果有人尝试撤销你的migration那么一个错误消息将会被显示it can’t be done”

4 Running Migrations

Rails provides a set of rake tasks to work with migrations which boils down to running certain sets of migrations. Rails提供给migrations一组rake任务,它大致归纳于运行若干的migrationsThe very first migration related rake task you use will probably be db:migrate.你使用的非常靠前的migration相应于rake的任务恰好是db:migrateIn its most basic form it just runs the up method for all the migrations that have not yet been run.其中它是最基本的形式,它仅仅运行migrations 还没被运行的up方法。If there are no such migrations it exits.如果没有这样的迁移它将会退出。

Note that running the db:migrate also invokes the db:schema:dump task, which will update your db/schema.rb file to match the structure of your database.注意运行db:migrate也调用了db:schema:dump任务,它将会更新你的db/schema.rb来和你的数据库匹配。

If you specify a target version, Active Record will run the required migrations (up or down) until it has reached the specified version.如果你特别(指定了)一个目标版本,Active Record将会运行所请求的migrationsupdown)直到它已经达成了(与)指定版本(的匹配)。The version is the numerical prefix on the migrations filename.版本号是migrations文件的数字前缀。For example to migrate to version 20080906120000 run例如迁移到20080906120000版本

$ rake db:migrate VERSION=20080906120000

If this is greater than the current version (i.e. it is migrating upwards) this will run the up method on all migrations up to and including 20080906120000, if migrating downwards this will run the down method on all the migrations down to, but not including, 20080906120000.如果执行的版本比当前版本更优(新)(i.e. it is migrating upwards)这将会执行包含20080906120000的所有版本的migrations up方法,反之则执行不包括20080906120000的版本之外的所有版本的migrations down方法。

4.1 Rolling Back

A common task is to rollback the last migration, for example if you made a mistake in it and wish to correct it.一个常用的任务来回滚最新的migration,例如如果你在其中犯了一个错误希望Rails能够改正它。Rather than tracking down the version number associated with the previous migration you can run

$ rake db:rollback

This will run the down method from the latest migration.这将会对最新的migration执行down方法。If you need to undo several migrations you can provide a STEP parameter:如果你需要撤销一系列的migrations你可以提供一个STEP 参数:

$ rake db:rollback STEP=3

will run the down method from the last 3 migrations.将会对最近的3次迁移执行down方法。

The rake db:migrate:redo task is a shortcut for doing a rollback and then migrating back up again. rake db:migrate:redo任务是一个回滚后再次返回的快捷操作。As with the db:rollback task you can use the STEP parameter if you need to go more than one version back, for example通过dbrollback 任务你可以使用STEP参数如果你需要撤销超过一个版本,例如

$ rake db:migrate:redo STEP=3

Neither of these Rake tasks do anything you could not do with db:migrate, they are simply more convenient方便since you do not need to explicitly specify the version to migrate to.(通过)上两次rake任务,你没有对db:migrate任何事情,如果你不需要指定明确的版本来迁移这样做都非常方便。

Lastly, the db:reset task will drop the database, recreate it and load the current schema into it.最后,

db:reset任务将会drop数据库,重建它并且在其中导入正确的架构。

This is not the same as running all the migrationssee the section on schema.rb.这和运行所有的migrations不一样——阅读schema.rb章节。

4.2 Being Specific开始指定的

If you need to run a specific migration up or down the db:migrate:up and db:migrate:down tasks will do that. 如果你需要对一个指定的版本migration执行updown,那么db:migrate:up and db:migrate:down任务会满足你。Just specify the appropriate version and the corresponding migration will have its up or down method invoked, for example指定适当的版本那么相应的migration就会调用它的updown方法,例如

$ rake db:migrate:up VERSION=20080906120000

will run the up method from the 20080906120000 migration.将会执行来自20080906120000版本的migrationup方法。These tasks check whether the migration has already run, so for example db:migrate:up VERSION=20080906120000 will do nothing if Active Record believes that 20080906120000 has already been run.这个任务会检查migration是否已经运行,如果Active Record认为已经被运行过了db:migrate:up VERSION=20080906120000将不会做任何事情。

4.3 Being Talkative开始唠叨

By default migrations tell you exactly what they’re doing and how long it took.通过默认的migrations会明确地告诉你他们做了什么以及花费了多长时间。A migration creating a table and adding an index might produce output like this一次迁移创建一个表并且添加一个index可能会产生这样的输出

20080906170109 CreateProducts: migrating

create_table(:products)

–> 0.0021s

add_index(:products, :name)

–> 0.0026s

20080906170109 CreateProducts: migrated (0.0059s)

Several methods are provided that allow you to control all this:一些方法(被证明)允许你像下面这样控制

  • suppress_messages takes a block as an argument and suppresses抑制any output generated by the block. suppress_messages获取一个block作为一个参数并且抑制常规输出到block
  • say takes a message argument and outputs it as is. A second boolean argument can be passed to specify whether to indent缩进or not. say获取一个message参数并且输出它。第二个布尔参数可以指定是否缩进
  • say_with_time outputs text along with how long it took to run its block. If the block returns an integer it assumes it is the number of rows affected. say_with_time在文字旁边输出运行这个代码块花费了多少时间。如果block返回一个整数它假设这是受影响的代码行。

For example, this migration例如,这个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

generates the following output一般情况会得到如下输出

== CreateProducts: migrating =================================================

— Created a table

–> and an index!

— Waiting for a while

–> 10.0108s

–> 250 rows

== CreateProducts: migrated (10.0171s) =======================================

If you just want Active Record to shut up then running rake db:migrate VERBOSE=false will suppress抑制all output.如果你希望让所有Active Record闭嘴,那么运行rake db:migrate VERBOSE=false会抑制所有的输出。

5 Using Models in Your Migrations在你的migration中使用Moldels

When creating or updating data in a migration it is often tempting to use one of your models新建或更新一个migration的数据它通常会使你的models更有诱惑力。After all they exist to provide easy access to the underlying底层data.毕竟他们的存在提供了对底层数据的访问。This can be done, but some caution注意should be observed观察.这是可行的,但是有些地方需要注意。

For example, problems occur when the model uses database columns which are (1) not currently in the database and (2) will be created by this or a subsequent migration.例如,问题焦点当models使用数据库字段,但是migration1)在数据库中没有这个字段而migration2)将会被它本身的migration文件或者随后的migration创建。

Consider this example, where Alice and Bob are working on the same code base which contains a Product model:思考这个例子,AliceBob工作在同样的代码中,其主要包含一个Product模型:

Bob goes on vacation. Bob在休假

Alice creates a migration for the products table which adds a new column and initializes it. She also adds a validation to the Product model for the new column. Alice创建一个products表单的migration,其添加一个新的字段并初始化它。她也添加一个关于新字段的验证到Product模型。

rails generate migration AddFlagToProduct flag:int#然后再添加一部分这样通过创建器的话model文件也一起创建了

# db/migrate/20100513121110_add_flag_to_product.rb

 

class AddFlagToProduct < ActiveRecord::Migration

def change

add_column :products, :flag, :int

Product.all.each { |f| f.update_attributes!(:flag => ‘false’) }#初始化置为false

end

end

 

# app/model/product.rb

 

class Product < ActiveRecord::Base

validates :flag, :presence => true

end

Alice adds a second migration which adds and initializes another column to the products table and also adds a validation to the Product model for the new column.Alice添加第二个migration,在这个migration中给Products表单添加并初始化了另一个字段,而且为Product model的新字段添加并初始化了验证。

# db/migrate/20100515121110_add_fuzz_to_product.rb

 

class AddFuzzToProduct < ActiveRecord::Migration

def change

add_column :products, :fuzz, :string

Product.all.each { |f| f.update_attributes! :fuzz => ‘fuzzy’ }

end

end

# app/model/product.rb

 

class Product < ActiveRecord::Base

validates :flag, :fuzz, :presence => true

end

Both migrations work for Alice.两个migrations都是Alice编写的。

Bob comes back from vacation假期and:Bob休假回来然后接着下面的工作

  1. updates the sourcewhich contains both migrations and the latests version of the Product model. 更新源代码——包含migrations和最新版本的Product模型。
  2. runs outstanding migrations with rake db:migrate, which includes the one that updates the Product model. 通过rake db:migrate执行突出(最新版本的migration)的migrations,其中包含了更新Product模型。

The migration crashes because when the model attempts尝试to save, it tries to validate the second added column, which is not in the database when the first migration runs.数据迁移冲突因为当model尝试保存时,它试图验证第二个添加的字段,然而它在第一次migration运行时不在数据库中。(没有弄明白呢感觉好像两个迁移是分开的才会出现这样的错误)

rake aborted!

An error has occurred, this and all later migrations canceled:

 

undefined method `fuzz' for #<Product:0x000001049b14a0>

A fix for this is to create a local model within the migration.解决这个错误是在migration创建一个本地的modelThis keeps rails from running the validations, so that the migrations run to completion.这样在rails运行中保持了验证,使得migrations完成了验证。

When using a faux model, it’s a good idea to call Product.reset_column_information to refresh the ActiveRecord cache for the Product model priorto updating data in the database.当使用一个人造的model,调用Product.reset_column_information来刷新在更新数据到数据库前的Product modelActiveRecord缓存是一个好主意。

If Alice had done this instead, there would have been no problem:如果Alice这样做了就不会有问题了

# db/migrate/20100513121110_add_flag_to_product.rb

 

class AddFlagToProduct < ActiveRecord::Migration

class Product < ActiveRecord::Base

end

 

def change

add_column :products, :flag, :int

Product.reset_column_information

Product.all.each { |f| f.update_attributes!(:flag => false) }

end

end

 

class AddFuzzToProduct < ActiveRecord::Migration

class Product < ActiveRecord::Base

end

def change

add_column :products, :fuzz, :string

Product.reset_column_information

Product.all.each { |f| f.update_attributes! :fuzz => ‘fuzzy’ }

end

end

 

6 Schema Dumping and You架构模式和你

6.1 What are Schema Files for?

Migrations, mighty as they may be, are not the authoritative source for your database schema. Migrations,很可能不是你的数据库schema的授权源。That role falls to either db/schema.rb or an SQL file which Active Record generates by examining the database.这一规则影响到db/schema.rbSQL文件它是Active Record创建器通过检查数据库生成的。They are not designed to be edited, they just represent the current state of the database.他们的设计(原意)是不被编辑的,他们仅仅是当前数据库状态的一个表现。

There is no need (and it is error prone) to deploy部署a new instance of an app by replaying the entire整个migration history. It is much simpler and faster to just load into the database a description of the current schema.这里没有必要(并且是错误倾向)去通过replaying整个migration历史来部署一个app的新的实例。更明智和更快捷的方式是load数据库的描述(也就是)当前schema

For example, this is how the test database is created: the current development database is dumped (either to db/schema.rb or db/development.sql) and then loaded into the test database.例如,这是测试数据库怎样被创建的:当前的开发数据库已经被转储(要么db/schema.rb 要么db/development.sql)然后被导入test数据库。

Schema files are also useful if you want a quick look at what attributes an Active Record object has. Schema文件在你希望快速的查看Active Record object有些什么属性的时候也很有帮助。This information is not in the model’s code and is frequently spread across several migrations but is all summed up in the schema file. 这些信息不是model中的代码并且频繁分布在多个migrations但是是所有架构文件的总结。The annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarizing the schema, may also be of interest. annotate_models 插件,它能够自动在每个model汇总的顶部的添加(更新)comments,可能你会有兴趣。

6.2 Types of Schema Dumps架构转储的形式

There are two ways to dump the schema. This is set in config/application.rb by the config.active_record.schema_format setting, which may be either :sql or :ruby.有两种方式来转储架构。这是(相关)设置在config/application.rb通过config.active_record.schema_format来设置,其形式要么是:sql要么是:ruby.

If :ruby is selected then the schema is stored in db/schema.rb. If you look at this file youll find that it looks an awful可怕lot like one very big migration:如果:ruby被选中那么schema被存储在db/schema.rb。如果你查看这个文件你将会发现它看起来就像一个很大的migration

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

In many ways this is exactly what it is.在很多情况下它能够准确的(反应出schema)的信息。This file is created by inspecting the database and expressing its structure using create_table, add_index, and so on.这个文件被创建来检查数据库以及表达它的结构使用create_table, add_index等等。Because this is database independent it could be loaded into any database that Active Record supports.由于数据库的独立性它可以被导入到Active Record支持的任何数据库中。This could be very useful if you were to distribute an application that is able to run against multiple databases.这会非常有用如果你发行一个应用程序它可能面对多个数据库运行。

There is however a trade-off: db/schema.rb cannot express database specific items such as foreign key constraints, triggers or stored procedures.然而,有一个权衡:DB/ schema.rb不能表达数据库的具体项目,如外键约束,触发器或存储过程。While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database.虽然在一个migration你可以执行自定义的SQL表达式,schema储存器不能修复一些SQL的表达式(不能实现migrationrollback等功能)。If you are using features like this then you should set the schema format to :sql.如果你正在使用这样的特性接着你应该设置schema 格式为:sql

Instead of using Active Records schema dumper the databases structure will be dumped using a tool specific to that database (via the db:structure:dump Rake task) into db/#{Rails.env}_structure.sql. For example for PostgreSQL the pg_dump utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.

By definition this will be a perfect copy of the database’s structure but this will usually prevent loading the schema into a database other than the one used to create it.根据定义,这将是一个完美的复制数据库的结构,但通常会阻止加载到比其他用于创建一个数据库的架构。

6.3 Schema Dumps and Source Control

Because schema dumps are the authoritative source for your database schema, it is strongly recommended建议that you check them into source control.因为架构转储是你的数据库架构的授权源,强烈建议你检查他们到源控制。

7 Active Record and Referential参照Integrity完整

The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.

Validations such as validates :foreign_key, :uniqueness => true are one way in which models can enforce data integrity. The :dependent option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.

Although Active Record does not provide any tools for working directly with such features, the execute method can be used to execute arbitrary SQL. There are also a number of plugins such as foreign_key_migrations which add foreign key support to Active Record (including support for dumping foreign keys in db/schema.rb).