Использование Rails Migration в другой базе данных, чем стандартное "производство" или "разработка",
У меня есть проект rails, который определяет стандартное производство:, разработка и: тестовые DB-соединения в config/database.yml
Кроме того, у меня есть quiz_development: и quiz_production: определение, указывающее на host_database/db/user/password
Теперь моя цель - определить Миграцию, которая использует "quiz_#{RAILS_ENV
}`" в качестве своей конфигурации базы данных.
Что я пробовал (и не смог):
- Настройка ActiveRecord:: Base.connection в файле миграции
- Изменение задачи db: migrate в rails для установки ActiveRecord:: Base.connection там
Вопрос:
Как я могу сделать rake db: migrate использовать другое определение базы данных?
Спасибо,
Франк
Ответы
Ответ 1
Немного поздно, но сегодня я столкнулся с этой проблемой, и я придумал эту обычную задачу:
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
Ответ 2
Там гораздо легче ответить. Добавьте это к своей миграции:
def connection
ActiveRecord::Base.establish_connection("quiz_#{Rails.env}").connection
end
Это для Rails 3.1. Для Rails 2.X или 3.0 вместо него используется функция класса (например, def self.connection
)
Ответ 3
Я получил это для работы со следующим кодом.
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 в конфигурации базы данных по умолчанию отслеживала контролируемые миграции в контроле версий для соответствующего проекта.
Мне не удалось выполнить миграцию вниз.
Ответ 4
Вы должны определить другие базы данных/среды в /config/environment.
После этого вы можете использовать следующую команду для переноса этой конкретной среды.
rake db:migrate RAILS_ENV=customenvironment
Ответ 5
Эй, я копался в этом несколько дней, и я закончил с этим решением, просто хотел поделиться им, это может помочь кому-то.
Вот полный смысл для него. https://gist.github.com/rafaelchiti/5575309
У этого есть детали и объяснение. Но найдите ниже более подробную информацию, если они вам понадобятся.
Подход основан на добавлении пространства имен к уже известным рейк-задачам db: migrate, db: create, db: drop и выполнять эти задачи с другой базой данных. А затем добавление класса активной активной записи (AR) для подключения на основе конфигурации нового файла database.yml. Таким образом, вам не нужно взламывать миграции с помощью соединений, и вы получаете чистую структуру каталогов.
Ваша структура закончится следующим образом
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. (пример в основном).
Best!.
Ответ 6
Для 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
Ответ 7
Следуя @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
Ответ 8
Недавно я боролся с той же проблемой. Цель состояла в том, чтобы разделить таблицу историй с другой базой данных, поскольку она была уже такой большой и все еще очень быстро развивалась.
Я начал пытаться разрешить это, выполнив ActiveRecord::Base.establish_connection(:history_database)
, но не смог получить никаких вариантов этого способа для работы без закрытия соединения. Затем, наконец, я нашел решение ниже.
В модели History после внесения этого изменения:
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 в исходной базе данных, чтобы миграция не выполнялась снова.
Ответ 9
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 db.
Если вы просто хотите, чтобы он был доступен в ваших миграциях, расширьте класс ActiveRecord::Migration
.
Ответ 10
В rails 3.2 добавление метода подключения к вашей миграции НЕ работает. Итак, все ответы вроде
def connection
@connection ||= ActiveRecord::Base.establish_connection
end
просто не работает (не может down
, не работает с change
, потерянного соединения и т.д.). Причина этого в том, что класс ActiveRecord:: Migration и Migrator имеет соединения жестко закодированные в ActiveRecord:: Base все через place.
К счастью этот пост указал мне на этот билет, который имеет хорошее решение, а именно переопределение фактической рейк-задачи.
В итоге я использовал немного другую задачу rake, чтобы я мог быть конкретным в отношении миграции, которую я запускал в другой базе данных (мы пытались поддерживать несколько версий db):
Здесь моя lib/task/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
Это позволяет нам переносить миграции в одну базу данных в свой собственный подкаталог (девять_four/migrations вместо db/migrations). Он также дает общую изоляцию каждой базы данных с точки зрения их схем и версий миграции. Единственный недостаток - запустить две задачи рейка (db: migrate и nine_four: db: migrate).
Ответ 11
В дополнение к выполнению миграции в другой среде, я также хочу, чтобы схемы были в отдельных файлах. Вы можете сделать это из командной строки:
RAILS_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate
Но мне нравится пользовательский подход к грабежной задаче, поэтому я могу набрать это вместо:
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
Ответ 12
Я нашел отличный способ сделать это:
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
Ответ 13
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 для выполнения.
Ответ 14
Вы можете использовать эту версию, которая также поддерживает 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
Ответ 15
Вы пытались использовать quiz_development как RAILS_ENV (вместо того, чтобы пытаться использовать его "quiz_#{RAILS_ENV}"
)?
RAILS_ENV=quiz_development rake db:migrate
Ответ 16
Вы также можете перенести все связанные с Quiz_ миграции в отдельную подпапку в каталоге db/, а затем добавить задачи rake, отражающие функции регулярной миграции, но которые ищут миграции в этом подкаталоге. Не супер-элегантный, возможно, но он работает. Вы можете копировать и вставлять задачи рейка уже в рельсы и немного модифицировать их.
Ответ 17
Основываясь на ответе @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
Ответ 18
если вы хотите отобразить сообщение Wordpress на веб-сайте rails и
вы не хотите использовать многомагистральный жемчуг. вы можете использовать приведенный ниже код, чтобы получить данные из блога 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
Ответ 19
Я получил эту работу, создав отдельные классы соединителей для разных баз данных и используя их в переносах.
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 поддерживает пул соединений, который является хешем, индексированным классом. Подробнее здесь. Поэтому использование отдельных классов для отдельных соединений защищает нас от ошибки закрытого соединения.
Кроме того, использование up
и down
вместо change
позволяет нам откатить миграцию без каких-либо проблем. Все еще не выяснили причину этого.