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:migrate。Active 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 Record’s functionality) it is database independent: you don’t 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。
You’ll 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_at和updated_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不限制更改schema。You 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.一些在model的migrations中的注意事项。
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.当数据库支持通过声明来改变数据库的结构(例如PostgreSQL和SQLite3),会在包含在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/migrate中。The 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添加并运行了20080906124500。Alice 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 Alice’s two migrations so rake db:migrate would run them (even though Bob’s 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 Alice’s migration removed a table that Bob’s migration assumed to exist, then trouble would certainly strike.例如如果Alice在migration中移除了一个表但是Bob’s的migraion假设它还在,那么麻烦就来了。
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接着你不可能再去编辑和运行这个(错误的)migraion:Rails认为(你已经)运行了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.model和generators创建器会为添加的新的model创建合适的migrations。This 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.timestamps
(
Active
Record
会自动生成
updated_at和
created_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_column和remove_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
进行破坏性的操作仍然使用老式的
up
和
down
方法。
This
is
because
Rails
doesn
’
t
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
(don
’
t
forget
to
update
the
corresponding
相应的
model)
or
if
you
don
’
t
want
a
primary
key
at
all
(for
example
for
a
HABTM
join
table)
you
can
pass
:id => false.
你可以更改主键的名字通过使用
:primary_key选项(不要忘记更新其相应的model)或者你根本不想要主键(例如添加一个HABTM关系到表中)你可以通过:id => false。If
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=BLACKHOLE到SQL声明中用于创建表单(当使用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。最后它重命名了:upccode
。
This 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 don’t 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变成remove,add_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_at和updated_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会给你添加id。If 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 of ‘Photo’.将会添加一个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 aren’t 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类方法)删除需要写进up和down的方法,那样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.如果你打算使用其他方法,你可以常规的写up和down方法。
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::IrreversibleMigration。If 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任务,它大致归纳于运行若干的migrations。The very first migration related rake task you use will probably be db:migrate.你使用的非常靠前的migration相应于rake的任务恰好是db:migrate。In 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将会运行所请求的migrations(up或down)直到它已经达成了(与)指定版本(的匹配)。The version is the numerical prefix on the migration’s filename.版本号是migration‘s文件的数字前缀。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通过db:rollback 任务你可以使用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
migrations
–
see
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执行up或down,那么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就会调用它的up或down方法,例如
$
rake
db:migrate:up
VERSION=20080906120000
will
run
the
up
method
from
the
20080906120000
migration.
将会执行来自
20080906120000
版本的
migration
的
up
方法。
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使用数据库字段,但是migration(1)在数据库中没有这个字段而migration(2)将会被它本身的migration文件或者随后的migration创建。
Consider this example, where Alice and Bob are working on the same code base which contains a Product model:思考这个例子,Alice和Bob工作在同样的代码中,其主要包含一个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
休假回来然后接着下面的工作
updates
the
source
–
which
contains
both
migrations
and
the
latests
version
of
the
Product
model.
更新源代码
——
包含
migrations
和最新版本的
Product
模型。
- 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创建一个本地的model。This 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 prior前to updating data in the database.当使用一个人造的model,调用Product.reset_column_information来刷新在更新数据到数据库前的Product model的ActiveRecord缓存是一个好主意。
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.rb和SQL文件它是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 you’ll 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的表达式(不能实现migration的rollback等功能)。If you are using features like this then you should set the schema format to :sql.如果你正在使用这样的特性接着你应该设置schema 格式为:sql。
Instead of using Active Record’s schema dumper the database’s 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).