2009-09-10 18 views
44

我有一個Rails項目運行它定義了標準的生產:,上使用Rails遷移:發展和:在配置測試DB-連接/ database.yml的不同的數據庫標準「生產」或「開發」

在另外我有一個quiz_development:和quiz_production:定義指向一個不同的主機/ db /用戶/密碼

我現在的目標是定義一個使用「quiz_#{RAILS_ENV}」的遷移作爲它的數據庫配置。

我曾嘗試(和失敗):

  • 設置的ActiveRecord :: Base.connection在遷移文件
  • 更改分貝:遷移任務,在軌設置的ActiveRecord :: Base.connection有

問:

我怎樣才能使耙分貝:遷移使用其他數據庫的定義是什麼?

感謝, 弗蘭克

回答

11

有點晚了,但我這個問題今天處理,我想出了這個定製的耙子任務:

namespace :db do 
    desc "Apply db tasks in custom databases, for example rake db:alter[db:migrate,test-es] applies db:migrate on the database defined as test-es in databases.yml" 
    task :alter, [:task,:database] => [:environment] do |t, args| 
    require 'activerecord' 
    puts "Applying #{args.task} on #{args.database}" 
    ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[args.database]) 
    Rake::Task[args.task].invoke 
    end 
end 
+0

謝謝,稍作更正:我更改了require'activerecord'以要求'active_record' – valk 2015-04-19 11:55:07

0

您是否嘗試過使用quiz_development作爲RAILS_ENV(而不是試圖得到它使用"quiz_#{RAILS_ENV}")?

RAILS_ENV=quiz_development rake db:migrate 
0

您也可以將您的所有quiz_有關遷移到在db /目錄下的子文件夾分開,然後添加耙任務鏡像經常遷移的功能,但是這會在該子目錄中的遷移。也許不是超優雅,但它的作品。你可以複製和粘貼已經在rails中的rake任務,只需稍微修改它們即可。

13

您應該在/ config/environments中定義其他數據庫/環境。

之後,您可以使用以下命令來遷移該特定環境。

rake db:migrate RAILS_ENV=customenvironment 
2

除了在不同的環境中運行的遷移,我也想獨立文件中的模式。你可以在命令行中執行此操作:

RAILS_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate 

但我喜歡自定義rake任務的辦法,所以我可以鍵入這個:

rake db:with[quiz_development, db:migrate] 

這裏的耙子任務:

namespace :db do 
    desc "Run :task against :database" 
    task :with, [:database,:task] => [:environment] do |t, args| 
    puts "Applying #{args.task} to #{args.database}" 
    ENV['SCHEMA'] ||= "#{Rails.root}/db/schema_#{args.database}.rb" 
    begin 
     oldRailsEnv = Rails.env 
     Rails.env = args.database 
     ActiveRecord::Base.establish_connection(args.database) 
     Rake::Task[args.task].invoke 
    ensure 
     Rails.env = oldRailsEnv 
    end 
    end 
end 
5
module ActiveRecord::ConnectionSwitch 
    def on_connection(options) 
    raise ArgumentError, "Got nil object instead of db config options :(" if options.nil? 
    ActiveRecord::Base.establish_connection(options) 
    yield 
    ensure 
    ActiveRecord::Base.establish_connection ActiveRecord::Base.configurations[Rails.env] 
    end 
end 

ActiveRecord.send :extend, ActiveRecord::ConnectionSwitch 

如果你把它放在config/initializers/裏面,你可以這樣做:

ActiveRecord.on_connection ActiveRecord::Base.configurations['production'] do 
    Widget.delete_all 
end 

這將刪除生產數據庫上的所有小部件,並確保連接到當前的Rails環境數據庫後重新建立。

如果您只是想讓它在您的遷移中可用,那麼insead可以擴展ActiveRecord::Migration類。

32

有一個更容易的答案。將此添加到您的遷移中:

def connection 
    ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection 
end 

這是針對Rails 3.1的。對於Rails 2.X或3.0,它是一個類功能,而不是(例如def self.connection

+2

這似乎接近真正的答案,但失敗並出現錯誤:關係「schema_migrations」不存在。 – kenn 2012-06-07 03:39:53

+1

我用這個,使用Rails 3.1.4,它確實運行遷移。但是,它不會執行向下遷移,並且(即使沒有試圖停止)運行rake db:migrate將繼續運行相同的遷移,但不能這樣做,因爲表中已經添加了該列。 – 2012-06-13 17:06:06

+0

有耙子不能重新運行遷移。請參閱下面的答案。 (不幸的是#{$!} stackoverflow不支持註釋中的代碼示例。) – 2012-06-13 17:55:03

18

我得到這個與以下代碼一起使用。

class AddInProgressToRefHighLevelStatuses < ActiveRecord::Migration 
    def connection 
    @connection = ActiveRecord::Base.establish_connection("sdmstore_#{Rails.env}").connection 
    end 

    def change 
    add_column :ref_high_level_statuses, :is_in_progress, :boolean, :default => true 

    @connection = ActiveRecord::Base.establish_connection("#{Rails.env}").connection 
    end 
end 

有必要設置連接回得到它寫遷移到schema_migrations表,以便耙不會試圖在下次重新運行遷移。這假定您希望默認數據庫配置中的schema_migrations表跟蹤爲相應項目檢入版本控制的遷移。

我無法讓下遷移工作。

+0

是的,這對於讓Bryan Larsen的解決方案起作用是非常必要的。謝謝 – muirbot 2012-10-03 22:48:35

+0

嗨,我仍然有這個問題。它工作正常,遷移並將遷移版本添加到主數據庫中的遷移表中,但隨後返回一個錯誤:'connection is closed' ...奇怪的是一切正常,除了這個錯誤。 – Tallmaris 2012-12-04 15:49:28

+0

@Tallmaris我完全有同樣的問題。你有沒有找到解決方案? – RajaRaviVarma 2013-05-23 11:37:20

7

嘿,我已經深入瞭解了這幾天,並且我最終得到了這個解決方案,只是想分享它,它可能會幫助某人。

這裏完整的要點。 https://gist.github.com/rafaelchiti/5575309 它有詳細的解釋。但是如果你需要它們,可以在下面找到更多細節

該方法基於向已知的rake任務db添加名稱空間:migrate,db:create,db:drop並使用不同的數據庫執行這些任務。然後根據新的database.yml文件的配置添加基礎活動記錄(AR)類以進行連接。這樣你就不需要用連接東西來解決遷移問題,並且你可以得到一個乾淨的目錄結構。

你的結構最終會像這樣

config 
    |- database.yml 
    \- another_database.yml (using the same nomenclature of 'development', 'test', etc). 

db 
    |- migrate (default migrate directory) 
    |- schema.rb 
    |- seed.rb 

another_db 
    |- migrate (migrations for the second db) 
    |- schema.rb (schema that will be auto generated for this db) 
    |- seed.rb (seed file for the new db) 

然後在你的代碼,你可以創建一個基類,從這個新的database.yml文件讀取配置,並連接到它僅在繼承模型那個AR基類。 (在要點中的例子)。

最好!

+0

當我們在application.rb中有config.active_record.schema_format =:sql時,運行rake store:db:migrate覆蓋當前的db/structure.sql,而不是更新db_store/structure.sql。任何想法爲什麼? – papdel 2013-10-16 05:46:23

+0

關於上面的註釋,將ENV ['SCHEMA']更改爲ENV ['DB_STRUCTURE']讓它工作。更多細節在這裏 - https://github.com/rails/docrails/blob/master/activerecord/lib/active_record/railties/databases.rake#L283 – papdel 2013-10-16 14:09:22

+0

@rafael你的Gist不再可用。你可以重新發布或延長你的答案,完整的詳細信息你如何做到這一點? – jwadsack 2015-06-23 16:10:49

0

基於@ TheDeadSerious的回答是:

module ActiveRecord::ConnectionSwitch 
    def on_connection(connection_spec_name) 
    raise ArgumentError, "No connection specification name specified. It should be a valid spec from database.yml" unless connection_spec_name 
    ActiveRecord::Base.establish_connection(connection_spec_name) 
    yield 
    ensure 
    ActiveRecord::Base.establish_connection(Rails.env) 
    end 
end 

ActiveRecord.send :extend, ActiveRecord::ConnectionSwitch 

用法:

ActiveRecord.on_connection "sdmstore_#{Rails.env}" do 
    Widget.delete_all 
end 
7

對於Rails的3.2,這就是我們所做的,與遷移上下的工作原理:

class CreateYourTable < ActiveRecord::Migration 

    def connection 
    @connection ||= ActiveRecord::Base.connection 
    end 

    def with_proper_connection 
    @connection = YourTable.connection 
    yield 
    @connection = ActiveRecord::Base.connection 
    end 


    def up 
    with_proper_connection do 
     create_table :your_table do |t| 
     end 
    end 
    end 

    def down 
    with_proper_connection do 
     drop_table :your_table 
    end 
    end 

end 
+1

我們做了類似的事情,但是通過一個帶有'#with_connection(connection)'方法的'ConnectionMigration'超類,所以我們可以在不同連接的不同遷移中使用這些功能而不會有重複。此外,不需要覆蓋'#連接'(請參閱http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-connection)。 – connec 2013-11-07 14:32:15

+0

注意:這不適用於'change'方法;你必須使用'up'和'down'。 – s2t2 2014-03-08 23:38:26

+0

如果要單獨遷移,請參閱http://stackoverflow.com/a/16542724/670433 – s2t2 2014-03-09 00:38:59

8

繼@Bryan Larsen之後,如果您使用抽象類將一系列模型附加到不同的數據庫,並希望與建立類似的模型

class CreatePosts < ActiveRecord::Migration 
    def connection 
     Post.connection 
    end 
    def up 
     ... 
    end 
end 

class Post < ReferenceData 
end 

class ReferenceData < ActiveRecord::Base 
    self.abstract_class = true 
    establish_connection "reference_data_#{Rails.env}" 
end 
1
class Article < ActiveRecord::Base 

    ActiveRecord::Base.establish_connection(
     :adapter => "mysql2", 
     :host  => "localhost", 
     :username => "root", 
     :database => "test" 
    ) 
end 

和:

class Artic < Aritcle 
    self.table_name = 'test' 

    def self.get_test_name() 
     query = "select name from testing" 
     tst = connection.select_all(query) #select_all is important! 
     tst[0].fetch('name') 
    end 
end 
遷移對他們的模式,那麼你就可以做到這一點

您可以調用Artic.get_test_name以執行。

0

如果你想顯示wordpress文章到你的rails網站和 你不想使用mult-magic連接gem。你可以使用下面的代碼來從wordpress博客獲取數據。

class Article < ActiveRecord::Base 

    ActiveRecord::Base.establish_connection(
    :adapter => "mysql2", 
    :host  => "localhost", 
    :username => "root", 
    :database => "blog" 
    ) 

    self.table_name = 'wp_posts' 

    def self.get_post_data() 
     query = "select name from testing" 
     tst = connection.select_all(query) 
     tst[0].fetch('name') 
    end 
end 
4

在rails 3.2中,向遷移添加連接方法不起作用。因此,所有的答案一樣

def connection 
@connection ||= ActiveRecord::Base.establish_connection 
end 

根本行不通的原因是(不能down,不change,連接丟失等工作)了的ActiveRecord ::遷移和遷移類具有硬編碼到ActiveRecord的連接:: Base allovertheplace

幸運的是this post指向我this ticket這有一個很好的解決方案,即覆蓋實際的rake task

最後我用一個稍微不同的rake任務,這樣我就可以具體談談我在不同的數據庫上運行遷移(我們試圖支持多個數據庫版本):

這裏是我的lib /任務/數據庫.rake

# Augment the main migration to migrate your engine, too. 
task 'db:migrate', 'nine_four:db:migrate' 

namespace :nine_four do 
    namespace :db do 
     desc 'Migrates the 9.4 database' 
     task :migrate => :environment do 
      with_engine_connection do 
       ActiveRecord::Migrator.migrate("#{File.dirname(__FILE__)}/../../nine_four/migrate", ENV['VERSION'].try(:to_i)) 
      end 
     end 
    end 
end 

# Hack to temporarily connect AR::Base to your engine. 
def with_engine_connection 
    original = ActiveRecord::Base.remove_connection 
    ActiveRecord::Base.establish_connection("#{ Rails.env }_nine_four") 
    yield 
ensure 
    ActiveRecord::Base.establish_connection(original) 
end 

這使我們能夠把具體到一個數據庫遷移在自己的子目錄(nine_four /遷移,而不是DB /遷移)。 它還爲每個數據庫在其模式和遷移版本方面提供完全隔離。唯一的缺點是有兩個rake任務要運行(db:migrate和nine_four:db:migrate)。

+0

將'schema.rb' /'structure.sql'與遷移分離是至關重要的,所有其他響應看起來都會錯過。 [This SO post](http://stackoverflow.com/a/22609887/201911)更進一步,並添加了其他有用的rake任務。 – jwadsack 2015-06-23 16:23:48

1

你可以使用這個版本,同時也支持rake db:rollback

class ChangeQuiz < ActiveRecord::Migration 
    def connection 
    ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection 
    end 

    def reset_connection 
    ActiveRecord::Base.establish_connection(Rails.env) 
    end 

    def up 
    # make changes 

    reset_connection 
    end 

    def self.down 
    # reverse changes 

    reset_connection 
    end 
end 
2

,我發現了一個偉大的清潔方式做到這一點:

class CreateScores < ActiveRecord::Migration 

    class ScoresDB < ActiveRecord::Base 
    establish_connection("scores_#{Rails.env}") 
    end 

    def connection 
    ScoresDB.connection 
    end 

    def up 
    create_table :scores do |t| 
     t.text :account_id 
     t.text :offer 
    end 
    end 

    def down 
    drop_table :scores 
    end 
end 
8

最近我同樣的問題掙扎。目標是將歷史表分成不同的數據庫,因爲它已經非常龐大,並且仍然在迅速增長。

我開始嘗試通過執行ActiveRecord::Base.establish_connection(:history_database)來解決它,但未能關閉連接,無法獲得該方式的任何變化。最後我發現了下面的解決方案。

class History < ActiveRecord::Base 

    # Directs queries to a database specifically for History 
    establish_connection :history_database 

    ... 
end 

我能夠做到這一點在遷移它完美地工作:

class CreateHistoriesTableInHistoryDatabase < ActiveRecord::Migration 
    def up 
    History.connection.create_table :histories do |t| 
     ... 
    end 
    end 

    def down 
    History.connection.drop_table :histories 
    end 
end 

這將在不同的數據庫中創建表

在歷史模型進行此更改後,但修改原始數據庫中的schema_migrations表,以便遷移不會再次運行。

+1

適用於導軌5 – neck 2017-05-24 19:39:20

0

我通過爲不同的數據庫創建單獨的連接器類並在遷移中使用它們來實現此目的。

class AddExampleToTest < ActiveRecord::Migration 
    def connection 
    @connection = OtherDatabaseConnector.establish_connection("sdmstore_#{Rails.env}").connection 
    end 
    def up 
    add_column :test, :example, :boolean, :default => true 

    @connection = MainDatabaseConnector.establish_connection("#{Rails.env}").connection 
    end 
    def down 
    remove_column :test, :example 

    @connection = MainDatabaseConnector.establish_connection("#{Rails.env}").connection 
    end 
end 

我們可以在初始化器中定義這些連接器類。

class MainDatabaseConnector < ActiveRecord::Base 
end 
class OtherDatabaseConnector < ActiveRecord::Base 
end 

ActiveRecord :: Base保留一個連接池,該連接池是由該類索引的哈希。 Read more here。所以使用單獨的類來進行單獨的連接可以保護我們免受封閉連接錯誤

另外,使用updown而不是change可讓我們回滾遷移,而不會有任何問題。還沒有弄清楚這個原因。

相關問題