在Ruby on Rails中使用迁移

时间:2020-03-06 14:35:35  来源:igfitidea点击:

我想确认以下分析是正确的:

我正在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"调用加载大量外部提供的数据,但是我发现迁移系统非常方便。