使用Rails在不同的数据库迁移比标准的“生产”或“开发”
我有一个rails项目运行,它定义了config / database.yml中的标准生产::development和test DB连接
另外我有一个quiz_development:和quiz_production:定义指向一个不同的主机/数据库/用户/密码
我现在的目标是定义一个使用“ quiz_#{RAILS_ENV
}`”作为其数据库configuration的迁移。
我所尝试过的(并失败了):
- 在迁移文件中设置ActiveRecord :: Base.connection
- 在rails中更改db:migrate任务,在那里设置ActiveRecord :: Base.connection
题:
我如何使耙db:迁移使用其他数据库定义?
谢谢,弗兰克
有点晚了,但我今天正在处理这个问题,我想出了这个自定义rake任务:
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
有一个更容易的答案。 将此添加到您的迁移:
def connection ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection end
这是Rails 3.1。 对于Rails的2.X或3.0它是一个类的function,而不是(例如def self.connection
)
我得到这个与下面的代码工作。
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表中,这样rake不会在下次尝试重新运行迁移。 这假设您希望默认数据库configuration中的schema_migrations表跟踪为相应项目检入版本控制的迁移。
我无法使下迁移工作。
您应该在/ config / environments中定义其他数据库/环境。
之后,您可以使用以下命令来迁移该特定的环境。
rake db:migrate RAILS_ENV=customenvironment
嘿,我已经钻了这几天,我结束了这个解决scheme,只是想分享它,它可能会帮助别人。
这里完整的要点。 https://gist.github.com/rafaelchiti/5575309它有详细的解释。; 但是如果你需要的话,可以find更多的细节。
该方法基于向已知的rake任务db:migrate,db:create,db:drop添加命名空间,并使用不同的数据库执行这些任务。 然后根据新build的database.yml文件的configuration添加一个基本活动logging(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)
然后在你的代码中,你可以创build一个基类并从这个新的database.yml文件中读取configuration,并且只在从那个AR基类inheritance的模型上连接它。 (在要点中的例子)。
最好!。
对于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
继@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
我最近也遇到了同样的问题。 目标是将历史表分成不同的数据库,因为它已经非常庞大,而且还在迅速增长。
我开始尝试通过执行ActiveRecord::Base.establish_connection(:history_database)
来解决它,但是不能在没有closures连接的情况下获得任何这种方式的变化。 最后我发现了下面的解决scheme。
在进行此更改后的历史logging模型中:
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
这将在不同的数据库中创build表,但是会修改原始数据库中的schema_migrations表,以避免迁移再次运行。
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 env的数据库之后重新build立。
如果你只是想在你的迁移中使用insead,请扩展ActiveRecord::Migration
类。
在rails 3.2中,向你的迁移添加一个连接方法是行不通的。 所以所有的答案都是这样的
def connection @connection ||= ActiveRecord::Base.establish_connection end
简直不会工作(不能down
,不能change
,连接丢失等)原因是ActiveRecord :: Migration和Migrator类有连接硬编码到ActiveRecord :: Base 遍布 的 地方 。
幸运的是这篇文章指出了这张票有一个很好的解决scheme,即覆盖实际的佣金任务 。
我最终使用了一个稍微不同的rake任务,以便我可以具体说明在不同数据库上运行的迁移(我们试图支持多个db版本):
这是我的lib /任务/ database.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 / migrations而不是db / migrations)。 它还为每个数据库提供完全隔离的架构和迁移版本。 唯一的缺点是有两个rake任务运行(db:migrate和nine_four:db:migrate)。
除了在不同的环境中运行迁移之外,我还希望将这些模式放在单独的文件中。 您可以从命令行执行此操作:
RAILS_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate
但是我喜欢自定义rake任务方法,所以我可以input这个:
rake db:with[quiz_development, db:migrate]
这是rake任务:
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
我发现了一个很好的方法来做到这一点:
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
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来执行。
你可以使用这个版本,它也支持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
您是否尝试过使用quiz_development作为RAILS_ENV(而不是试图让它使用"quiz_#{RAILS_ENV}"
)?
RAILS_ENV=quiz_development rake db:migrate
您还可以将所有与测验相关的迁移移动到db /目录中的单独子文件夹中,然后添加镜像常规迁移function的rake任务,但会查找该子目录中的迁移。 也许不超级优雅,但它的作品。 你可以复制和粘贴已经在Rails中的耙子任务,只是修改一下。
基于@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
如果你想显示wordpress文章到你的rails网站,你不想使用多魔术连接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