2009-09-10 14 views
44

Tengo un proyecto en ejecución rieles que define la producción estándar :,: desarrollo y: prueba DB-conexiones en config/database.ymlEl uso de rieles migración en diferentes bases de datos de la "producción" estándar o "desarrollo"

En Además tengo un quiz_development: y quiz_production: definición apuntando a un host/db/usuario/contraseña differnet

Mi objetivo ahora es definir una migración que use "quiz_#{RAILS_ENV}` "como configuración de la base de datos.

Lo que he intentado (y fracasado):

  • Configuración ActiveRecord :: Base.connection en el archivo de migración
  • Cambio de la db: migrate tarea en los carriles para establecer ActiveRecord :: Base.connection existe

pregunta:

¿Cómo puedo hacer rake db: migrate uso que otra definición de base de datos?

Gracias, Frank

Respuesta

11

Un poco tarde, pero que estaba tratando con este problema hoy y se me ocurrió con esta tarea rastrillo personalizado:

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

Gracias, con una pequeña corrección: he cambiado requiere 'activerecord' para requerir 'active_record' – valk

0

¿Ha intentado utilizar quiz_development como RAILS_ENV (en lugar de tratar de conseguir que se utilice "quiz_#{RAILS_ENV}")?

RAILS_ENV=quiz_development rake db:migrate 
0

También puede mover todas sus migraciones relacionadas quiz_ en una subcarpeta independiente en el directorio db/y luego añadir tareas rake que refleja la funcionalidad de la migración regular, sino que busca el migraciones en ese subdirectorio. No súper elegante quizás pero funciona. Puede copiar y pegar las tareas de rake que ya están en rieles y simplemente modificarlas un poco.

13

Debe definir las otras bases de datos/entornos en/config/environments.

Después de eso, puede usar el siguiente comando para migrar ese entorno específico.

rake db:migrate RAILS_ENV=customenvironment 
2

Además de ejecutar una migración en un entorno diferente, también quiero los esquemas en archivos separados.Puede hacerlo desde la línea de comandos:

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

pero me gusta el enfoque de la tarea rake personalizado, de modo que puedo escribir esto en su lugar:

rake db:with[quiz_development, db:migrate] 

Aquí está la tarea 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 
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 

Si coloca esto dentro de config/initializers/, podrá hacer algo como esto:

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

Esto eliminará todos los widgets en el db de producción y se asegurará de que la conexión con el db de env de envios actual se restablezca después de eso.

Si solo desea que esté disponible en sus migraciones, inserte extienda la clase ActiveRecord::Migration.

32

Hay una respuesta mucho más fácil. Agregue esto a su migración:

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

Eso es para Rails 3.1. Para Rails 2.X o 3.0 es una función de clase en su lugar (por ejemplo, def self.connection)

+2

Esto parece cercano a la respuesta real, pero falla con un error: la relación "schema_migrations" no existe. – kenn

+1

Lo usé, usando Rails 3.1.4, y de hecho ejecuté la migración. Sin embargo, no realizará la migración hacia abajo y (incluso sin intentarlo) ejecutando rake db: migrate sigue ejecutando esta misma migración, lo que no ocurre porque la tabla ya tiene esa columna agregada. –

+0

Obtenido rake para no volver a ejecutar la migración. Ver mi respuesta a continuación. (Desafortunadamente # {$!} Stackoverflow no admite ejemplos de código en los comentarios.) –

18

Me puse a trabajar con el siguiente código.

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 

Era necesario establecer la conexión de nuevo para conseguir que escribir la migración a la mesa schema_migrations modo de rastrillo no trataría de volver a ejecutar la migración de la próxima vez. Esto supone que desea que la tabla schema_migrations en la configuración de base de datos predeterminada realice un seguimiento de las migraciones registradas en el control de versión para el proyecto correspondiente.

No pude obtener la migración hacia abajo al trabajo.

+0

Sí, esto es absolutamente necesario para que la solución de Bryan Larsen funcione.Gracias – muirbot

+0

Hola, todavía tengo problemas con esto. Está funcionando bien, migrando y agregando la versión de migración a la tabla de migración en el DB principal, pero luego regresa con un error: 'connection is closed' ... extrañamente todo funciona, excepto por este error. – Tallmaris

+0

@Tallmaris Tengo exactamente el mismo problema. ¿Has encontrado alguna solución para esto? – RajaRaviVarma

7

Hola, he estado investigando esto por unos días y terminé con esta solución, solo quería compartirla, podría ayudar a alguien.

Aquí la esencia completa para ello. https://gist.github.com/rafaelchiti/5575309 Tiene detalles y una explicación. Pero encuentra a continuación más detalles si los necesitas.

El enfoque se basa en agregar un espacio de nombres a las tareas de rake ya conocidas db: migrate, db: create, db: drop y realizar esas tareas con una base de datos diferente. Y luego, al agregar una clase base de registro activo (AR) para la conexión basada en la configuración del nuevo archivo database.yml. De esta forma no es necesario hackear las migraciones con la conexión y se obtiene una estructura de directorios limpia.

Su estructura va a terminar como esto

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) 

Luego, en su código puede crear una clase base y leer la configuración de este nuevo archivo database.yml y conectarse a él sólo en los modelos que heredan de esa clase base AR. (ejemplo en la esencia).

¡Mejor !.

+0

Cuando tenemos config.active_record.schema_format =: sql en application.rb, ejecutamos un almacén de rake: db: migrate sobrescribe el actual db/structure.sql en lugar de actualizar db_store/structure.sql. ¿Alguna idea de por qué? – papdel

+0

Con respecto al comentario anterior, cambiar ENV ['SCHEMA'] a ENV ['DB_STRUCTURE'] lo puso en funcionamiento. Más detalles aquí: https://github.com/rails/docrails/blob/master/activerecord/lib/active_record/railties/databases.rake#L283 – papdel

+0

@rafael your Gist ya no está disponible. ¿Puede volver a publicarlo o ampliar su respuesta con detalles completos sobre cómo lo hizo? – jwadsack

0

Sobre la base de la respuesta 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 

Uso:

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

para los carriles de 3,2, esto es lo que hicimos, trabaja con la migración de arriba a abajo:

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

Hicimos algo similar, pero con una superclase 'ConnectionMigration' con un método' #with_connection (connection) ', por lo que podríamos usar la funcionalidad en diferentes migraciones con diferentes conexiones sin duplicación. Además, la anulación para '# connection' no es necesaria (ver http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#method-i-connection). – connec

+0

NOTA: esto no funciona con el método 'change'; tienes que usar 'arriba' y 'abajo'. – s2t2

+0

si quiere migrar por separado, vea http://stackoverflow.com/a/16542724/670433 – s2t2

8

A continuación de @Bryan Larsen, si está utilizando una clase abstracta para adjuntar una serie de modelos a una base de datos diferente, y le gustaría migrar esquemas en ellos, entonces usted puede hacer esto:

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

con un modelo establecido algo como:

class Post < ReferenceData 
end 

y

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 

Y:

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 

Puede llamar a Artic.get_test_name para ejecutar.

0

si desea mostrar la publicación de wordpress en el sitio web de sus raíles y no desea utilizar la gema de conexión multimagia. puede usar el siguiente código para obtener los datos del blog de 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

En los carriles 3.2, no es posible agregar un método de conexión a su migración. Así que todas las respuestas como

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

simplemente no va a funcionar (no puede down, no funciona con change, pérdida de conexión, etc.) La razón de esto es que el ActiveRecord :: Migración y Migrator la clase tiene conexiones codificadas en ActiveRecord :: Base allovertheplace.

Afortunadamente this post me señaló this ticket que tiene una buena solución, a saber, anulando el actual rake task.

Terminé usando una tarea rastrillo ligeramente diferente para que pudiera ser específico acerca de las migraciones que se ejecutan en la base de datos diferente (estábamos tratando de soportar múltiples versiones db):

Aquí está mi lib/task/base de datos .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 

Esto nos permite colocar las migraciones específicas de una base de datos en su propio subdirectorio (nine_four/migraciones en lugar de db/migraciones). También da a cada base de datos aislamiento total en términos de sus versiones de esquema y migración. El único inconveniente es tener dos tareas de rake para ejecutar (db: migrate y nine_four: db: migrate).

+0

Separar el 'schema.rb' /' structure.sql' y las migraciones son críticas y todas las otras respuestas parecen perderse esto . [Esta publicación de SO] (http://stackoverflow.com/a/22609887/201911) lleva esto un paso más allá y agrega otras tareas útiles de rake. – jwadsack

1

Usted podría utilizar esta versión, que también es compatible con 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

he encontrado una gran manera limpia de hacer esto:

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

Recientemente he luchado con el mismo problema. El objetivo era dividir una tabla de historias en una base de datos diferente, ya que ya era muy grande y seguía creciendo muy rápidamente.

Comencé a tratar de resolverlo haciendo ActiveRecord::Base.establish_connection(:history_database), pero no pude obtener ninguna variación de esa manera para trabajar sin que se cerrara la conexión. Entonces finalmente descubrí la solución a continuación.

En el modelo de la historia después de hacer este cambio:

class History < ActiveRecord::Base 

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

    ... 
end 

yo era capaz de hacer esto en la migración y funcionó perfectamente:

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

    def down 
    History.connection.drop_table :histories 
    end 
end 

Esto creará la tabla en una base de datos diferente , pero modifique la tabla schema_migrations en la base de datos original para que la migración no vuelva a ejecutarse.

+1

funciona para rieles 5 – neck

0

Funcioné creando clases de conectores separadas para diferentes bases de datos y usándolas en las migraciones.

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 

Podemos definir estas clases de conector en los inicializadores.

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

ActiveRecord :: Base mantiene un grupo de conexiones que es un hash indexado por la clase. Read more here. Entonces, usar clases separadas para conexiones separadas nos protege del error de conexión cerrada.

Además, utilizar up y down en lugar de change nos permite deshacer la migración sin ningún problema. Todavía no he descubierto la razón de esto.

Cuestiones relacionadas