it-swarm.com.de

Verwenden der Rails-Migration in einer anderen Datenbank als Standard "Produktion" oder "Entwicklung"

Ich habe ein Rails-Projekt, das die Standardproduktion definiert :,: Entwicklung und: Testen von DB-Verbindungen in config/database.yml

Außerdem habe ich eine quiz_development: und quiz_production: Definition, die auf ein anderes Host/db/user/password verweist

Mein Ziel ist es nun, eine Migration zu definieren, die "quiz_#{Rails_ENV}` "als Datenbankkonfiguration verwendet.

Was ich versucht habe (und versagt habe):

  • Festlegen von ActiveRecord :: Base.connection in der Migrationsdatei
  • Ändern der Task "db: migrate" in Rails, um dort ActiveRecord :: Base.connection festzulegen

Frage:

Wie kann ich rake db: migrate dazu veranlassen, diese andere Datenbankdefinition zu verwenden?

Danke, Frank

46
thenoseman

Ein bisschen spät, aber ich habe mich heute mit diesem Problem befasst und mir diese benutzerdefinierte Rake-Aufgabe ausgedacht:

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
11
Siu

Es gibt eine viel einfachere Antwort. Fügen Sie dies Ihrer Migration hinzu:

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

Das ist für Rails 3.1. Für Rails 2.X oder 3.0 ist es stattdessen eine Klassenfunktion (zB def self.connection)

35
Bryan Larsen

Ich habe dieses mit dem folgenden Code arbeiten können.

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

Die Verbindung musste zurückgesetzt werden, damit die Migration in die Tabelle schema_migrations geschrieben wird, sodass Rake die Migration beim nächsten Mal nicht erneut ausführen würde. Dies setzt voraus, dass die Tabelle schema_migrations in der Standarddatenbankkonfiguration die in der Versionskontrolle für das entsprechende Projekt eingecheckten Migrationen aufzeichnet.

Ich konnte die Abwärtsmigration nicht zum Laufen bringen.

18
Marlin Pierce

Sie sollten die anderen Datenbanken/Umgebungen in/config/Umgebungen definieren.

Danach können Sie den folgenden Befehl verwenden, um diese bestimmte Umgebung zu migrieren.

rake db:migrate Rails_ENV=customenvironment
13
Bitterzoet

Ich hatte vor kurzem mit dem gleichen Problem zu kämpfen. Ziel war es, eine Histories-Tabelle in eine andere Datenbank aufzuteilen, da sie bereits so groß war und immer noch sehr schnell wächst.

Ich habe versucht, das Problem mit Hilfe von ActiveRecord::Base.establish_connection(:history_database) zu lösen, konnte jedoch keine Abwandlungen dieser Methode erhalten, ohne dass die Verbindung geschlossen wurde. Dann entdeckte ich schließlich die Lösung unten.

Im History-Modell nach dieser Änderung:

class History < ActiveRecord::Base

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

  ...
end

Ich konnte das in der Migration machen und es hat perfekt funktioniert:

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

  def down
    History.connection.drop_table :histories
  end
end

Dadurch wird die Tabelle in einer anderen Datenbank erstellt, jedoch wird die Tabelle schema_migrations in der ursprünglichen Datenbank geändert, sodass die Migration nicht erneut ausgeführt wird.

8
MTarantini

Nach @Bryan Larsen: Wenn Sie eine abstrakte Klasse verwenden, um eine Serie von Modellen an eine andere Datenbank anzuhängen, und Schemas darauf migrieren möchten, können Sie Folgendes tun:

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

mit einem Modell so etwas einrichten:

class Post < ReferenceData
end

und

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

Hey, ich habe mich ein paar Tage damit beschäftigt, und ich habe diese Lösung gefunden. Ich wollte sie nur teilen, sie könnte jemandem helfen.

Hier der komplette Gist dafür. https://Gist.github.com/rafaelchiti/5575309 .__ Es enthält Details und Erklärungen. Unten finden Sie weitere Details, wenn Sie sie benötigen.

Der Ansatz basiert auf dem Hinzufügen eines Namensraums zu den bereits bekannten Rake-Aufgaben db: migrate, db: create, db: drop und führt diese Aufgaben mit einer anderen Datenbank aus. Und dann beim Hinzufügen einer AR-Klasse (Base Active Record) für die Verbindung basierend auf der Konfiguration der neuen Datei database.yml. Auf diese Weise müssen Sie die Migrationen nicht mit Verbindungssachen umgehen und erhalten eine saubere Verzeichnisstruktur.

Ihre Struktur wird so enden

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)

Anschließend können Sie in Ihrem Code eine Basisklasse erstellen und die config aus dieser neuen database.yml-Datei lesen und nur für die Modelle eine Verbindung herstellen, die von dieser AR-Basisklasse erben. (Beispiel in der Gist).

Beste!.

7
Rafael

Für Rails 3.2 haben wir dies mit der Migration nach oben und unten durchgeführt:

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
zephyr
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

Wenn Sie dies in config/initializers/ platzieren, können Sie Folgendes tun: 

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

Dadurch werden alle Widgets auf der Produktions-DB gelöscht und es wird sichergestellt, dass die Verbindung zur aktuellen Rails-Env-DB wieder hergestellt wird.

Wenn Sie es nur in Ihren Migrationen verfügbar machen möchten, erweitern Sie die Klasse ActiveRecord::Migration durch Insead.

5
TheDeadSerious

In Rails 3.2 funktioniert das Hinzufügen einer Verbindungsmethode zu Ihrer Migration NICHT

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

funktioniert einfach nicht (kann down nicht, funktioniert nicht mit change, Verbindung verloren usw.) Der Grund dafür ist, dass die ActiveRecord :: Migration- und Migrator-Klasse Verbindungen haben hartcodiert nach ActiveRecord :: Base allovertheplace .

Zum Glück dieser Beitrag hat mich auf dieses Ticket hingewiesen, das eine gute Lösung bietet, nämlich das Überschreiben des tatsächlichen Rechenaufgabe .

Am Ende habe ich eine etwas andere Rake-Aufgabe verwendet, damit ich genau wissen kann, welche Migrationen ich für die verschiedenen Datenbanken durchführe (wir haben versucht, mehrere Datenbankversionen zu unterstützen):

Hier ist meine 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

Auf diese Weise können wir Migrationen, die für eine Datenbank spezifisch sind, in ein eigenes Unterverzeichnis stellen (nine_four/migrations anstelle von db/migrations). Es bietet außerdem eine vollständige Isolierung jeder Datenbank hinsichtlich ihres Schemas und ihrer Migrationsversion. Der einzige Nachteil besteht darin, dass zwei Rake-Tasks ausgeführt werden müssen (db: migrate und nine_four) : db: migrieren).

4
Ryan

Neben einer Migration in einer anderen Umgebung möchte ich auch, dass die Schemas in separaten Dateien gespeichert werden. Sie können dies von der Kommandozeile aus tun:

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

Aber ich mag den benutzerdefinierten Rake-Task-Ansatz, sodass ich stattdessen Folgendes eingeben kann:

rake db:with[quiz_development, db:migrate]

Hier ist die Rechenaufgabe:

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
2
Eric Dobbs

Ich habe einen tollen, sauberen Weg gefunden, dies zu tun:

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
2
just.jules

Sie können diese Version verwenden, die auch rake db:rollback unterstützt:

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
1
nlsrchtr
class Article < ActiveRecord::Base

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

Und:

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

Sie können Artic.get_test_name aufrufen, um auszuführen.

Basierend auf der Antwort von @ 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

Verwendungszweck:

ActiveRecord.on_connection "sdmstore_#{Rails.env}" do
  Widget.delete_all
end
0
Pratik Khadloya

Haben Sie versucht, quiz_development als Rails_ENV zu verwenden (anstatt zu versuchen, "quiz_#{Rails_ENV}" zu verwenden)?

Rails_ENV=quiz_development rake db:migrate
0
hgmnz

Ich habe dieses Problem gelöst, indem ich separate Connector-Klassen für verschiedene Datenbanken erstellt und sie bei den Migrationen verwendet habe.

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

Wir können diese Connector-Klassen in Initialisierern definieren.

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

ActiveRecord :: Base verwaltet einen Verbindungspool, bei dem es sich um einen von der Klasse indizierten Hash handelt. Lesen Sie hier mehr . Die Verwendung separater Klassen für separate Verbindungen schützt uns also vor dem Fehler der geschlossenen Verbindung.

Wenn Sie up und down anstelle von change verwenden, können Sie die Migration ohne Probleme zurücksetzen. Ich habe immer noch nicht den Grund dafür herausgefunden.

0
gauravm31

Sie können auch alle Ihre mit Quiz_ zusammenhängenden Migrationen in einen separaten Unterordner im Verzeichnis db/verschieben und dann Rake-Tasks hinzufügen, die die regulären Migrationsfunktionen widerspiegeln, die jedoch nach den Migrationen in diesem Unterverzeichnis suchen. Nicht besonders elegant, aber es funktioniert. Sie können die Rake-Aufgaben, die bereits in Rails vorhanden sind, kopieren und einfügen und sie nur ein wenig ändern.

0
ealdent

wenn Sie den WordPress-Post auf Ihrer Rails-Website und anzeigen möchten, möchten Sie kein multizauberhaftes Verbindungsedelstein verwenden. Sie können den folgenden Code verwenden, um die Daten aus dem WordPress-Blog zu erhalten.

 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