web-dev-qa-db-fra.com

Utilisation de Rails Migration sur une base de données différente de la "production" ou du "développement"

J'ai un projet Rails en cours d'exécution qui définit la production standard:,: développement et: test des connexions DB dans config/database.yml

De plus, j'ai un quiz_development: et quiz_production: définition pointant vers un hôte/db/utilisateur/mot de passe différent

Mon objectif est maintenant de définir une migration qui utilise "quiz_#{Rails_ENV}` "comme configuration de base de données.

Ce que j'ai essayé (et échoué):

  • Définition d'ActiveRecord :: Base.connection dans le fichier de migration
  • Modification de la tâche db: migrate dans Rails pour y définir ActiveRecord :: Base.connection

Question:

Comment puis-je faire en sorte que rake db: migrate utilise cette autre définition de base de données?

Merci, .__ Frank

46
thenoseman

Un peu tard, mais je traitais ce problème aujourd'hui et je suis arrivé avec cette tâche de rake personnalisée:

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

Il y a une réponse beaucoup plus facile. Ajoutez ceci à votre migration:

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

C'est pour Rails 3.1. Pour Rails 2.X ou 3.0, il s’agit plutôt d’une fonction de classe (par exemple, def self.connection)

35
Bryan Larsen

Je dois travailler avec le code suivant.

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

Il était nécessaire de rétablir la connexion pour qu'il écrive la migration dans la table schema_migrations afin que rake n'essaye pas de réexécuter la migration la prochaine fois. Cela suppose que vous souhaitiez que la table schema_migrations de la configuration de base de données par défaut garde la trace des migrations archivées dans le contrôle de version du projet correspondant.

Je ne pouvais pas obtenir la migration vers le bas au travail.

18
Marlin Pierce

Vous devez définir les autres bases de données/environnements dans/config/environnements.

Après cela, vous pouvez utiliser la commande suivante pour migrer cet environnement spécifique.

rake db:migrate Rails_ENV=customenvironment
13
Bitterzoet

J'ai récemment lutté avec le même problème. Le but était de scinder une table d'histoires en une autre base de données, car elle était déjà très grande et continuait de croître très rapidement.

J'ai commencé à essayer de le résoudre en faisant ActiveRecord::Base.establish_connection(:history_database), mais je ne pouvais faire fonctionner aucune variante de cette façon sans que la connexion soit fermée. Enfin, j'ai découvert la solution ci-dessous.

Dans le modèle d'historique après avoir effectué ce changement:

class History < ActiveRecord::Base

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

  ...
end

J'ai pu faire cela dans la migration et cela a parfaitement fonctionné:

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

  def down
    History.connection.drop_table :histories
  end
end

Cela créera la table dans une base de données différente, mais modifiera la table schema_migrations dans la base de données d'origine pour que la migration ne s'exécute plus.

8
MTarantini

Dans le prolongement de @Bryan Larsen, si vous utilisez une classe abstraite pour attacher une série de modèles à une base de données différente et souhaitez migrer des schémas sur ces derniers, procédez comme suit:

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

avec un modèle mis en place quelque chose comme:

class Post < ReferenceData
end

et

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

Hey, je me suis plongé dans cette affaire pendant quelques jours et je me suis retrouvé avec cette solution, je voulais juste la partager, cela pourrait aider quelqu'un.

Voici le Gist complet pour cela. https://Gist.github.com/rafaelchiti/5575309 Il contient des détails et des explications. Mais trouvez ci-dessous plus de détails si vous en avez besoin.

L'approche consiste à ajouter un espace de noms aux tâches rake déjà connues, db: migrate, db: create, db: drop et à effectuer ces tâches avec une base de données différente. Et ensuite, en ajoutant une classe d'enregistrement actif (AR) de base pour la connexion en fonction de la configuration du nouveau fichier database.yml. De cette façon, vous n'avez pas besoin de pirater les migrations avec des trucs de connexion et vous obtenez une structure de répertoire vierge.

Votre structure finira comme ça

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)

Ensuite, dans votre code, vous pouvez créer une classe de base, lire la configuration à partir de ce nouveau fichier database.yml et vous y connecter uniquement sur les modèles hérités de cette classe de base AR. (exemple dans le Gist).

Meilleur!.

7
Rafael

Pour Rails 3.2, voici ce que nous avons fait, fonctionne avec la migration vers le haut et le bas:

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

Si vous placez ceci dans config/initializers/, vous pourrez faire quelque chose comme ceci: 

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

Cela supprimera tous les widgets de la base de production et garantira que la connexion à la base de données de l'environnement Rails actuel est rétablie par la suite.

Si vous souhaitez simplement le rendre disponible dans vos migrations, insead étend la classe ActiveRecord::Migration.

5
TheDeadSerious

En plus d'exécuter une migration dans un environnement différent, je souhaite également que les schémas soient dans des fichiers séparés. Vous pouvez le faire depuis la ligne de commande:

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

Mais j'aime bien l'approche de la tâche de rake personnalisée pour pouvoir taper ceci à la place:

rake db:with[quiz_development, db:migrate]

Voici la tâche de 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
2
Eric Dobbs

J'ai trouvé un excellent moyen de faire ceci:

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

Vous pouvez utiliser cette version, qui prend également en charge 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
1
nlsrchtr
class Article < ActiveRecord::Base

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

Et:

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

Vous pouvez appeler Artic.get_test_name pour l'exécuter.

Basé sur la réponse de @ 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

Usage:

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

Avez-vous essayé d'utiliser quiz_development en tant que Rails_ENV (au lieu d'essayer de le faire utiliser "quiz_#{Rails_ENV}")?

Rails_ENV=quiz_development rake db:migrate
0
hgmnz

Cela fonctionne en créant des classes de connecteur distinctes pour différentes bases de données et en les utilisant dans les migrations.

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

Nous pouvons définir ces classes de connecteur dans les initialiseurs.

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

ActiveRecord :: Base conserve un pool de connexions qui est un hachage indexé par la classe. Lire la suite ici . Donc, utiliser des classes séparées pour des connexions séparées nous protège de l'erreur de connexion fermée.

De plus, utiliser up et down au lieu de change nous permet d’annuler la migration sans aucun problème. Je n'ai toujours pas compris la raison de cela.

0
gauravm31

Vous pouvez également déplacer toutes vos migrations liées à quiz_ dans un sous-dossier distinct du répertoire db /, puis ajouter des tâches rake reflétant la fonctionnalité de migration normale, mais qui recherchent les migrations dans ce sous-répertoire. Pas super élégant peut-être mais ça marche. Vous pouvez copier et coller les tâches rake déjà dans Rails et les modifier un peu.

0
ealdent

si vous souhaitez afficher la publication wordpress sur votre site Web Rails et que vous ne voulez pas utiliser le joyau de la connexion multi-magique. vous pouvez utiliser le code ci-dessous afin d’obtenir les données de wordpress blog.

 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