在Ruby on Rails中使用迁移
我想确认以下分析是正确的:
我正在RoR中构建一个Web应用程序。我为我的postgres db设计了一个数据结构(大约70个表;该设计可能需要在开发过程中进行更改和添加,以反映Rails的工作方式。例如,我设计了一些用户表和角色表,但是使用Restful Authentication是否有意义,我将对其进行清理并替换为RA要求的任何内容。)。
我有一个shellscript,该脚本调用一系列.sql文件,以用表和初始数据(例如Towns被预填充Towns)以及测试数据(例如Companies得到一些虚拟公司)填充空数据库。有数据可玩)。
例如:
CREATE TABLE towns ( id integer PRIMARY KEY DEFAULT nextval ('towns_seq'), county_id integer REFERENCES counties ON DELETE RESTRICT ON UPDATE CASCADE, country_id integer REFERENCES countries ON DELETE RESTRICT ON UPDATE CASCADE NOT NULL, name text NOT NULL UNIQUE );
命题0:数据比应用程序持续更长的时间,因此我坚信尽管缺乏DRYNESS,但我仍希望在数据库级别以及RoR模型中实施参考完整性。
主张1:如果我用Migrations替换脚本和sql文件,则目前无法在迁移代码中告诉我的Postgres数据库外键和当前在SQL DDL文件中设置的其他约束。
命题2:迁移的吹捧优势是对架构的更改与RoR模型代码一起进行了版本控制。但是,如果我将脚本和.sql文件保存在railsapp / db中,则可以轻松地对其进行版本控制。
命题3:鉴于迁移缺乏我想要的功能,并且提供了我可以复制的利益,因此我几乎没有理由考虑使用它们。所以我应该--skipmigrations在脚本/生成模型时间。
我的问题:如果接受0号提案,那么1,2,3号提案是对还是错,为什么?
谢谢!
解决方案
命题1是错误的:如果仅通过在迁移内部使用直接SQL,则可以肯定地使用迁移来定义参照完整性,有关更多详细信息,请参见此文章。
命题2:吹捧的迁移兴趣是能够渐进地定义数据库模型,同时跟踪每个变更的内容,并能够在以后方便地回滚任何此类变更。
我们必须小心创建/修改事物的顺序,但是可以做到。
要记住的一件事:rails更适合以应用程序为中心的设计。在Rails Way(tm)中,只能通过应用程序活动记录层访问数据库,并使用Web服务将数据公开给外部
命题1在至少两种情况下是错误的,我们可以使用诸如foreign_key_migrations之类的插件来执行以下操作:
def self.up create_table :users do |t| t.column :department_id, :integer, :references => :departments end end
这会在数据库中创建适当的外键约束。
当然,我们可能还需要在DDL中执行其他操作,在这种情况下,第二种情况变得更加引人注目:我们不必在迁移中使用Ruby DSL。尝试使用execute方法,而不是:
def self.up execute 'YOUR SQL HERE' end
这样,我们就可以将SQL脚本的内容保留在迁移中,从而获得后者的好处(最主要的是down
方法,我们在原始问题中未解决),并保留了我们喜欢的较低级别的控件。
1:我们可能想要试用此插件。虽然我自己没有尝试过,但似乎可以通过迁移添加外键约束。
2:迁移的真正好处是可以在数据库的历史记录中来回移动。使用.sql文件不是那么容易。
3:看看上面提到的插件是否对我们有用,然后决定:)无论如何,如果我们不使用它们,那就不是大笔资金了!
由于我们使用的是Postgres,并且可能不想安装foreign_key_migrations插件,因此当我想同时使用迁移和外键约束时,这就是我要做的事情。
我在ActiveRecord :: SchemaStatements中添加了一个名为" add_fk_constraint"的SchemaStatements方法。
这可以放在一些集中式文件中,但是在下面的示例迁移文件中,我只是将其内联。
module ActiveRecord module ConnectionAdapters # :nodoc: module SchemaStatements # Example call: # add_fk_constraint 'orders','advertiser_id','advertisers','id' # "If you want add/alter a 'orders' record, then its 'advertiser_id' had # better point to an existing 'advertisers' record with corresponsding 'id'" def add_fk_constraint(table_name, referencing_col, referenced_table, referenced_col) fk_name = "#{table_name}_#{referencing_col}" sql = <<-ENDSQL ALTER TABLE #{table_name} ADD CONSTRAINT #{fk_name} FOREIGN KEY (#{referencing_col}) REFERENCES #{referenced_table} (#{referenced_col}) ON UPDATE NO ACTION ON DELETE CASCADE; CREATE INDEX fki_#{fk_name} ON #{table_name}(#{referencing_col}); ENDSQL execute sql end end end end class AdvertisersOrders < ActiveRecord::Migration def self.up create_table :advertisers do |t| t.column :name, :string, :null => false t.column :net_id, :integer, :null => false t.column :source_service_id, :integer, :null => false, :default => 1 t.column :source_id, :integer, :null => false end create_table :orders do |t| t.column :name, :string, :null => false t.column :advertiser_id, :integer, :null => false t.column :source_id, :integer, :null => false end add_fk_constraint 'orders','advertiser_id','advertisers','id' end def self.down drop_table :orders drop_table :advertisers end end
我希望这对某人有帮助。这对我非常有用,因为我需要使用SQL" COPY"调用加载大量外部提供的数据,但是我发现迁移系统非常方便。