Rails Migrations Ⅲ

16.5 Advanced Migrations --- 高级的 migration 迁移

大多数Rails开发者使用migration迁移的基本功能来创建和管理数据库schema。但是,常常只需在向前一小步,就会让migration迁移更有用。本节讨论一些高级migration迁移用法。
一、Using Native SQL --- 使用原生SQL
Migration迁移给你一个不依赖数据库管理应用程序schema的方式。但是,如果migration迁移没有包含你需要的,能够完成你需要完成工作的方法,你将需要编写指定的数据库代码。要这样做,可使用execute()方法。
我常用的migration迁移例子是给子表添加一个外键约束。在创建line_items表时我们见过它。
class CreateLineItems < ActiveRecord::Migration
def self.up
create_table :line_items do |t|
t.column :product_id, :integer, :null => false
t.column :order_id, :integer, :null => false
t.column :quantity, :integer, :null => false
t.column :total_price, :integer, :null => false
end
execute "alter table line_items add constraint fk_line_item_products
foreign key (product_id) references products(id)"
execute "alter table line_items add constraint fk_line_item_orders
foreign key (order_id) references orders(id)"
end
def self.down
drop_table :line_items
end
end
When you use execute( ), you might well be tying your migration to a specific database engine: SQL you pass as a parameter to execute( ) uses your database’s native syntax.
The execute( ) method takes an optional second parameter. This is prepended to the log message generated when the SQL is executed.
二、Extending Migrations --- 扩展Migration迁移
如果你观察前一节的line item 迁移,你可能对两个execute()语句的重复部分感到奇怪。将外键的创建包含到helper方法内是很好的抽象。
我们可以通过添加一个方法来做到,如下面的migration迁移源文件。
def self.foreign_key(from_table, from_column, to_table)
constraint_name = "fk_#{from_table}_#{from_column}"
execute %{alter table #{from_table}
add constraint #{constraint_name}
foreign key (#{from_column})
references #{to_table}(id)}
end
(self. 是必须的,因为migration迁移是做为一个类方法运行的,所以我们需要在这个上下文环境内调用foreign_key())
up()迁移内,我们可以使用下面来调用这个新方法
def self.up
create_table ...
end
foreign_key(:line_items, :product_id, :products)
foreign_key(:line_items, :order_id, :orders)
end
虽然,我们可能希望更进一步,使我们的foreign_key()方法能对所有的migration迁移有效。要做到这,可在应用程序的lib目录内创建一个模型,并添加foreigh_key()方法。但这次,让它做为一个常规的实例方法,而不是类方法。
module MigrationHelpers
def foreign_key(from_table, from_column, to_table)
constraint_name = "fk_#{from_table}_#{from_column}"
execute %{alter table #{from_table}
add constraint #{constraint_name}
foreign key (#{from_column})
references #{to_table}(id)}
end
end
现在,你可以添加下面行到你的migration迁移文件顶部,以在任何migration迁移内包括它。
require "migration_helpers"
class CreateLineItems < ActiveRecord::Migration
extend MigrationHelpers
require行会在migration迁移代码内产生模型定义,并且extend行会添加MigrationHelpers模板的方法做为migration迁移内的类方法。你可以使用这种技术来开发,共享任何数量的migration迁移helper方法。
(并且,如果你想让你的生活更轻松,有些人写了一个插件,它自动管理添加外键约束。)
16.6 When Migrations Go Bad

 
Migration迁移经受着一系列问题。更新数据库schema的基础DDL语句不是事务的。这不是Rails的错误 --- 数据库对创建表,更新表和其它DDL语句不支持回滚。
让我们观察试图给数据库添加两个表的一个migration迁移。
class ExampleMigration < ActiveRecord::Migration
def self.up
create_table :one do ...
end
create_table :two do ...
end
end
def self.down
drop_table :two
drop_table :one
end
end
在常规的事件过程中,up()方法添加表onetwo,并且用drop()方法移除它们。
但如果在创建第二个表出现了问题时会发生什么呢?最终数据库会包含表one,但不包含表two。我们可以在migration迁移内修改这个问题,但现在我们却不能应用它 --- 如果我们试着用了,它将失败,因为表one已经存在了。
我们可以试试回滚migration迁移,但这没有用:因为原有的migration迁移失败了,数据库内的schema版本并没有更新,所以Rails不会尝试进行回滚。
从这点上说,你得浪费些时间用手工更改schema信息并删除表one。但或许不值得这样做。在这些情况下,我们的建议是简单地删除整个数据库,然后再重建它,并应用migration迁移来生成回溯数据。你不会丢失任何东西,并且你会知道你有个一致的shema
16.7 Schema Manipulation Outside Migrations
本章描述的所有migration迁移方法也可有效地做为活动记录的connection对象方法,并且可以在Rails应用程序的模型,视图,控制器内访问。
For example, you might have discovered that a particular long-running report runs a lot faster if the orders table has an index on the city column. However, that index isn’t needed during day-to-day running of the application, and tests have shown that maintaining it slows the application appreciably.
让我们写个方法,它创建索引,运行一个代码块,然后删除索引。它可是模型内的一个私有方法,或者是一个库内实现。
def run_with_index(column)
connection.add_index(:orders, column)
begin
yield
ensure
remove_index(:orders, column)
end
end
The statistics gathering method in the model can use this as follows:
def get_city_statistics
run_with_index(:city) do
# .. calculate stats
end
end
16.8 Managing Migrations
有些东西会降低migration迁移的作用。随着时间推移,你的schema定义将会分散到大量的单个migration迁移文件内,这么多的文件会影响你schema内每个表的定义。当这发生时,它会变得很难准确查看包含的每个表。这儿有一些让生活更轻松的建议。
有些team不能用于单独的migration迁移以获取所有的schema版本。相反,它们保持migration迁移文件在每个表内,其它migration迁移文件加载development数据到这些表中。当它们需要修改schema(比如说给表添加列),为那个表编辑现有的migration迁移文件。然后,它们清除并重新创建数据库,并重复应用所有的migration迁移。依据这个途径,它们总是可以在那个表的migration迁移文件内看到每个表的整个定义。
To make this work in practice, each member of the team needs to keep an eye on the files that are modified when updating their local source code from the project’s repository. When a migration file changes, it’s a sign that the database schema needs to be recreated.
Although it seems like this scheme flies against the spirit of migrations, it actually works well in practice.
使用migration迁移的另一种途径是我们本章前面描述的,为schema的每次修改创建一个新的migration。为了保持对schema发展的跟踪,你可以使用annotate_models插件。在运行时,这个插件观察当前的schema并添加对每个表的描述在用于那个表的模型顶部。
使用下面命令来安装annotate_models插件:
depot> ruby script/plugin install
[url]http://svn.pragprog.com/Public/plugins/annotate_models[/url]
一旦安装完,你就可以在任何时候运行它:
depot> rake annotate_models
完成后,每个模型源文件将有个注释块,它文档化相应数据库表列。例如,对于Depot应用程序,文件line_item.rb文件的开头是:
# Schema as of June 12, 2006 15:45 (schema version 7)
#
# Table name: line_items
#
# id :integer(11) not null, primary key
# product_id :integer(11) default(0), not null
# order_id :integer(11) default(0), not null
# quantity :integer(11) default(0), not null
# total_price :integer(11) default(0), not null
#
class LineItem < ActiveRecord::Base
# ...
如果随后你修改了schema,只要重新运行Rake任务:注释块将被更新以反映当前数据库状态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值