ruby-on-rails - framework - tutorial django
Usar Rails Migration en una base de datos diferente a la "producción" o "desarrollo" estándar (19)
Tengo un proyecto de carriles en ejecución que define la producción estándar :,: desarrollo y: conexiones DB de prueba en config / database.yml
Además, tengo un quiz_development: y quiz_production: definición que apunta a un host / db / usuario / contraseña differnet
Mi objetivo ahora es definir una migración que use " quiz_#{RAILS_ENV
}` "como su configuración de base de datos.
Lo que he intentado (y fallé):
- Establecer ActiveRecord :: Base.connection en el archivo de Migración
- Cambiar la tarea db: migrate en rieles para establecer ActiveRecord :: Base.connection allí
Pregunta:
¿Cómo puedo hacer que rake db: migrate use esa otra definición de base de datos?
Gracias, Frank
¿Has intentado usar quiz_development como RAILS_ENV (en lugar de intentar que use "quiz_#{RAILS_ENV}"
)?
RAILS_ENV=quiz_development rake db:migrate
Además de ejecutar una migración en un entorno diferente, también quiero los esquemas en archivos separados. Puedes hacer esto desde la línea de comando:
RAILS_ENV=quiz_development SCHEMA=db/schema_quiz_development.rb rake db:migrate
Pero me gusta el enfoque personalizado de la tarea de rake, así que puedo escribir esto en su lugar:
rake db:with[quiz_development, db:migrate]
Aquí está la tarea 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
Basado en 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
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
En Rails 3.2, no es posible agregar un método de conexión a su migración. Entonces todas las respuestas como
def connection
@connection ||= ActiveRecord::Base.establish_connection
end
simplemente no funcionará (no se puede down
, no funciona con change
, conexión perdida, etc.) La razón para esto es que la clase ActiveRecord :: Migration and Migrator tiene conexiones codificadas de forma rígida en ActiveRecord :: Base en all over the place
Afortunadamente, esta publicación me señaló este ticket que tiene una buena solución, es decir, que reemplaza la tarea de rake real.
Terminé usando una tarea de rake ligeramente diferente para poder ser específico acerca de las migraciones que ejecuto en la base de datos diferente (estábamos tratando de admitir múltiples versiones db):
Aquí está mi 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
Esto nos permite poner las migraciones específicas a una base de datos en su propio subdirectorio (nine_four / migrations en lugar de db / migrations). También proporciona a cada base de datos un 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).
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 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. Lea más aquí . Entonces, usar clases separadas para conexiones separadas nos protege del error de conexión cerrada.
Además, el uso de up
y down
lugar de change
nos permite deshacer la migración sin ningún problema. Todavía no he descubierto la razón de esto.
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
)
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
Hola. Estuve 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 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.
Tu estructura terminará así
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 desde este nuevo archivo database.yml y conectarse solo a los modelos que heredan de esa clase base AR. (ejemplo en la esencia).
¡Mejor!.
Lo tengo para 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
Fue necesario restablecer la conexión para que escribiera la migración en la tabla schema_migrations para que rake no intente volver a ejecutar la migración 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 para trabajar.
Para Rails 3.2, esto es lo que hicimos, funciona con la migración hacia arriba y hacia 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
Puede usar esta versión, que también admite 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
Recientemente luché 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.
Empecé 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 Historia después de hacer este cambio:
class History < ActiveRecord::Base
# Directs queries to a database specifically for History
establish_connection :history_database
...
end
Pude 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 modificará la tabla schema_migrations en la base de datos original para que la migración no vuelva a ejecutarse.
Siguiendo con @Bryan Larsen, si está utilizando una clase abstracta para adjuntar una serie de modelos a una base de datos diferente y desea migrar esquemas en ellos, puede hacer esto:
class CreatePosts < ActiveRecord::Migration
def connection
Post.connection
end
def up
...
end
end
con un modelo configurado algo así como:
class Post < ReferenceData
end
y
class ReferenceData < ActiveRecord::Base
self.abstract_class = true
establish_connection "reference_data_#{Rails.env}"
end
También puede mover todas las migraciones relacionadas con el cuestionario a una subcarpeta separada en el directorio db / y luego agregar tareas de rake que reflejen la funcionalidad de migración normal, pero que busque las 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.
Un poco tarde, pero hoy estaba lidiando con este problema y se me ocurrió esta tarea personalizada de 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
si desea mostrar la publicación de WordPress en su sitio web de rieles y no desea usar 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
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.
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 las 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 los db env actuales de env se restablezca después de eso.
Si solo desea que esté disponible en sus migraciones, inserte la extensión de la clase ActiveRecord::Migration
.