My rails app. uses mysql database and I need to generate .sqlite3.databases. Is it possible to use activerecord and rails models for it?
We are trying now to use models namespaced by Remote:: module but by this way we can't start concurrent generators.
In your remote models, you want to connect to a separate database using #establish_connection:
# config/database.yml
remote_development:
adapter: sqlite3
database: db/development.sqlite3
remote_production:
adapter: sqlite3
database: /usr/local/remote/myapp.sqlite3
# app/models/remote_model.rb
class RemoteModel < ActiveRecord::Base
establish_connection "remote_#{Rails.env}"
self.abstract_class = true
end
# app/models/remote_user.rb
class RemoteUser < RemoteModel
end
Note the abstract_class setter: this means the class in question doesn't have an underlying table: it's used for configuration purposes only.
Related
I followed the
multiple databases with ActiveRecord guide to configure my development environment. I have 3 databases, 2 databases are primary and one is supposed to be a read-only replica.
My issue is that the read replica database is not being populated I have verified that data is correctly split among the 2 primary databases, I have 2 database schema files, and I do not have any errors in the application when doing get requests. The read replica database is empty however.
My thought was maybe something was wrong with my config for the automatic connection switching between read and write databases? The guide says that you need to setup the read-only replica database manually but doesn't give any instructions. I'm using Postgresql and Rails 7.0.3.
I had previously assumed that Rails would copy the data from the primary database to the replica but now that I'm typing this I'm wondering if that data replication is to be manually setup in Postgres somehow?
I'm not sure if both the primary_abstract_class and self.abstract_class = true are needed in application_record? Rails 7 adds the primary_abstract_class by default and the multi-database guide tells you to add self.abstract_class = true
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
self.abstract_class = true
connects_to database: { writing: :primary, reading: :primary }
end
app/models/offices_record.rb
class OfficesRecord < ApplicationRecord
self.abstract_class = true
# this isn't the name of the actual databases, but rather the primary/replica key in database.yml
connects_to database: { writing: :offices, reading: :offices_replica }
end
config/database.yml
development:
primary:
<<: *default
database: myapp_development
migrations_paths: db/migrate
offices:
<<: *default
database: myapp_offices_development
migrations_paths: db/offices_migrate
# replica user's database permissions should be set to read-only.
offices_replica:
<<: *default
database: myapp_offices_replica_development
username: read_only_user
password: R34dOnlyUs3rP#ssw0rd
replica: true
config/initializers/multi_db.rb
Rails.application.configure do
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end
The office class inherits from OfficesRecord instead of ApplicationRecord because it is in the 2nd primary database.
app/models/office.rb
class Office < OfficesRecord
end
How I configured Postgres
In Postgres a database backup of the myapp_offices_development database was taken (with no data) and restored over a new database to create the replica called myapp_offices_replica_development. I then ran the below to create a read-only user to access the read-only replica.
CREATE ROLE readaccess;
GRANT CONNECT ON DATABASE myapp_offices_replica_development TO readaccess;
GRANT USAGE ON SCHEMA public TO readaccess;
CREATE USER read_only_user WITH PASSWORD ‘R34dOnlyUs3rP#ssw0rd';
GRANT readaccess TO read_only_user;
I'm trying to figure out how to populate the database myapp_offices_replica_development with data from myapp_offices_development. Does Rails auto-magically handle this?
how are you? I've been having some troubles when I try to establish a connection to two databases in postres. I will try to describe the scenario:
This is my pg.yml:
default: &default
adapter: postgresql
encoding: unicode
pool: 5
host: localhost
user: xxxxx
password: xxxxx
development:
<<: *default
database: not_legacy
legacy:
<<: *default
database: legacy
This is my Legacy::Base class:
#/models/legacy/base.rb
require 'active_record'
require 'erb'
module Legacy
class Base < ActiveRecord::Base
self.abstract_class = true
conf_contents = File.read('config/pg.yml')
conf_evaluated = ::ERB.new(conf_contents).result
conf = YAML.load(conf_evaluated)
ActiveRecord::Base.establish_connection conf['legacy']
end
end
and this is my NotLegacy::Base class:
#/models/not_legacy/base.rb
require 'active_record'
require 'erb'
module NotLegacy
class Base < ActiveRecord::Base
self.abstract_class = true
conf_contents = File.read('config/pg.yml')
conf_evaluated = ::ERB.new(conf_contents).result
conf = YAML.load(conf_evaluated)
ActiveRecord::Base.establish_connection conf['not_legacy']
end
end
Also, I have two classes that inherit from the classes previously described.
This is Legacy::Company:
#/models/legacy/company.rb
require_relative 'base'
module Legacy
class Company < Base
self.table_name = "company"
end
end
and NotLegacy::Company:
#/models/not_legacy/company.rb
require_relative 'base'
module NotLegacy
class Company < Base
self.table_name = "company"
end
end
Now, if I go to the console and do something like(I'm printing conf value):
irb(main):001:0> load 'app/models/legacy/company.rb'
CONFS: {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "host"=>"localhost", "user"=>"xxxxx", "password"=>"xxxxx", "database"=>"legacy"}
=> true
irb(main):002:0> Legacy::Company.count
=> 8
irb(main):003:0> load 'app/models/not_legacy/company.rb'
CONFS: {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "host"=>"localhost", "user"=>"xxxxx", "password"=>"xxxxx", "database"=>"not_legacy"}
=> true
irb(main):004:0> NotLegacy::Company.count
=> 1
At this point everything seems to work correctly since in the legacy database there are 8 records for company and in the not_legacy database there is only 1 record. But if I call Legacy::Company again, I get:
irb(main):005:0> Legacy::Company.count
=> 1
irb(main):005:0> NotLegacy::Company.count
=> 1
It seems that the second connection (made to the not_legacy database) is overwriting the first one (the one made to the legacy database).
If anyone of you can explain me why this is happening and how to fix it I will be immensely grateful
Thanks.
First I would like to go with basic about how you are going through code flow,
When you load any class, its code is executed & methods defined, your connection is created through static code written & establish_connection is executed.
Single instant of rails app can have connection to single database
You better to write code to switch through different database to have access for different database connectivity.
Legacy::Company or NotLegacy::Company both models have same table name companies from different databases. But when you connect particular database both model (which got loaded) will point to companies table in current database.
So what is current database mean to both model ?
-> The one which is loaded last time by establish_connection.
So this is little tedious but it is working that if you deal with different databases, you have to keep switching from one database to another one.
on config/database.yml
other_base:
adapter: postgresql
encoding: unicode
pool: 5
host: localhost
user: xxxxx
password: xxxxx
on your model
class YourModel < ActiveRecord::Base
self.establish_connection :other_base
end
I am building a rails app to store many different, user-supplied database connections and execute arbitrary sql against each of them. I am representing each database connection string as an instance of a 'connection' model and want to be able to write a method to query the database represented by each connection, ideally using the activerecord ORM. However, my code as written overwrites the database connection for the entire connections table when I use the establish_connection method in the following code. How would you advise me to change the code to query an arbitrary database, without overwriting the connection for the whole Connections table?
class Connection < ActiveRecord::Base
validates_presence_of :host, :port, :db_name, :db_user, :db_password, :db_type
def connect
self.connection = ActiveRecord::Base.establish_connection(
adapter: self.db_type,
host: self.host,
database: self.db_name,
username: self.db_user,
password: self.db_password
)
end
end
A good way to do that is making a model for each database connection you need, and then make other models as subclasses of them. So, for example:
Define all the needed connections in database.yml (per-environment)
# DB 1
development:
adapter: mysql2
encoding: utf8
database: db_1
username: ****
password: ****
host: ********
pool: 5
...
# DB 2
db2_development:
adapter: mysql2
encoding: utf8
database: db_2
username: ****
password: ****
host: ********
pool: 5
...
# Same for production (and/or other environments)
production:
...
db2_production:
...
Define a "master" model for each database, which inherits from ActiveRecord::Base
# DB1
class DB1 < ActiveRecord::Base
self.abstract_class = true
end
# DB2
class DB2 < ActiveRecord::Base
self.abstract_class = true
establish_connection "db2_#{Rails.env}"
end
...
Now define all the database-specific models as subclasses of the models defined above, in this way:
# DB1 specific model
class DB1_model < DB1
# model logic here
end
# DB2 specific model
class DB2_model < DB2
# model logic here
end
...
And you're good to go.
In this way, you can connect to N databases in different environments (usually development, staging, preprod and production, but they may be different in your case).
Also, remeber that Rails will manage a pool of SQL connections for each database.
In the example above, Rails will open a maximum of 5 connections for each database, so total will be 10 (for a single instance of your application).
If you use Phusion Passenger or Unicorn, and spawn 8 application instances, total SQL connections will be (at maximum) 10*8 = 80.
is it possible to use 2 types of database in a Rails application?
I am using 2 databases - Postgres to store data which might not change much, and MongoDB
to store data which change dynamically.
is this valid approach? and is it possible to connect between these 2 databases and operate
in a single Rails application?
Please correct, if i am wrong.
regards,
Balan
yes this is possible here is an example from my old code (but here we use mysql for both the DB, but I think you can get an idea)
in database.yml file define two databases
development: &defaults
adapter: mysql
encoding: utf8
database: DB1
enable_call: true
username:
password:
host:
portal_development: &defaults
adapter: mysql
encoding: utf8
database: DB2
enable_call: true
username:
password:
host:
in your models have to base models coupled with above databases
portal_base.rb
class PortalBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "portal_#{Rails.env}"
def self.table_name_prefix
"DB1."
end
end
default_base.rb
class DefaultBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "#{Rails.env}"
def self.table_name_prefix
"DB2."
end
end
and derive your models accordingly
class Client < PortalBase
#code
end
Hope you have an idea now :)
Found solution :
We can use mongoid gem to achieve this.
Steps to install
1) Install mongoid by adding "gem mongoid" in Gemfile and running bundle command
2) Generate mongoid configuration by typing "rails g mongoid:config", this will create a
config file mongoid.yml near database.yml file, you can add the configuration to Mongo server
in this file.
Note : After adding Mongoid, all the models created will be created for MongoDB by default, you can specify --orm option to generate Models for Postgres.
I'm working on upgrading my project from rails 3.0.9 to rails 3.2.5. I'm using multiple databases and when running the migration in rails 3.2.5 everything runs ok but everything it's created in the default database instead of it's corresponding database.
I thing it's a problem with the connection pool but the bug with connection pool was fixed for rails 3.1.x
These are my models:
class Account < ActiveRecord::Base
establish_connection :accounts
end
class Patient < ActiveRecord::Base
end
:accounts it's the connection to the other database while the other classes uses the default connection:
this my database.yml (I'm using an external file so I can modify the database connection without changing the code).
development:
adapter: mysql2
encoding: utf8
reconnect: false
database: <%= database_config_file['database_dev'] %>
pool: 5
username: <%= database_config_file['username'] %>
password: <%= database_config_file['password'] %>
socket: /var/lib/mysql/mysql.sock
accounts:
adapter: mysql2
encoding: utf8
reconnect: false
database: <%= database_config_file['database_account'] %>
pool: 5
username: <%= database_config_file['username'] %>
password: <%= database_config_file['password'] %>
socket: /var/lib/mysql/mysql.sock
this are my migrations for the classes:
class CreateAccounts < ActiveRecord::Migration
def self.connection
Account.connection #Account model has a connection to the database I want
end
...
end
Hope someone can help
Your DB where you're planing to make migrations has to be specified under development key (unless you want to create new environment) and your old DB -- under, say, old_db in your yml-file.
Then I suggest you to create legacy_base.rb in your models/ folder with the following contents:
class LegacyBase < ActiveRecord::Base
self.abstract_class = true
establish_connection :old_db # points to your legacy DB
end
Then for each model that belongs to your old DB you have to replace:
class SomeModel < ActiveRecord::Base
with:
class SomeModel < LegacyBase
This way your 'heritage' will use your legacy DB and newly created models/migrations will use your new DB without any tricks.
I hope I understood your intentions.