Multiple database connection in rails 4 - ruby-on-rails

I've faced a trouble while implementing multiple database connections in a Rails 4 application. Along with the primary database connection I've created secondary connection by specifying details in database.yml.
secondary_base:
adapter: postgresql
encoding: unicode
host: localhost
database: secondary_db
pool: 5
username: postgres
password: postgres
Then created a model named SecondaryBase which holds the connection to that secondary database. Code given below:
secondary_base.rb
class SecondaryBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "secondary_base"
end
Then added two models Member and Subdomain.
member.rb
class Member < SecondaryBase
has_and_belongs_to_many :subdomains
end
subdomain.rb
class Subdomain < SecondaryBase
has_and_belongs_to_many :members
end
Now as you can see that Member and Subdomain models are related by has_and_belongs_to_many relationship. So when I'm trying to insert data to the join table named members_subdomains, It's now working and giving me the error something like that PG: Undefined table, though the table exits in secondary_db database. What I understood that rails is trying to find the members_subdomains table in primary database.But when I tried the same code in Rails 3.2.13, then there were no problem at all, everything is fine. Have any of you deal with a similar problem before? Please help.

I believe you will need to switch from has_and_belongs_to_many to has_many :through to get this to work. In order to do that, just create a model for the join table - I'll call it `MemberSubdomain'.
class SecondaryBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "secondary_base"
end
class MemberSubdomain < SecondaryBase
self.table_name = 'members_subdomains'
end
class Member < SecondaryBase
has_many :member_subdomains
has_many :subdomains, :through => :member_subdomains, :source => :subdomain
end
class Subdomain < SecondaryBase
has_many :member_subdomains
has_many :members, :through => :member_subdomains, :source => :member
end

Related

How can I migrate a has_many / belongs_to relationship in rails to has_and_belongs_to_many?

I have an existing rails 6 application where I have two models:
class Reservation << ApplicationRecord
# ...
has_many :charges
# ...
end
class Charge << ApplicationRecord
# ...
belongs_to :reservation
# ...
end
I want to refactor it to this:
class Reservation << ApplicationRecord
# ...
has_and_belongs_to_many :charges
# ...
end
class Charge << ApplicationRecord
# ...
has_and_belongs_to_many :reservation
# ...
end
What I want to know is how to write that migration? There's already data in the table, so I need to retain existing charges whose reservation IDs are set and keep the link.
Be careful here, and make sure you can revert if there's a mistake, so you don't lose your data!
First you need to create the join table with a migration. You can create the migration from the command-line with:
rails g migration create_charges_reservations
this should create the template of the migration for you in db/migrate, which you'll populate according to your need like this:
class CreateChargesReservations < ActiveRecord::Migration[6.0]
def change
create_table charges_reservations do |t|
t.integer :charge_id
t.integer :reservation_id
end
end
end
run the migration from the command line:
rails db:migrate
Now make a join model:
# app/models/charges_reservation.rb
class ChargesReservation < ApplicationRecord
belongs_to :charge
belongs_to :reservation
end
Now you have to migrate the existing data, so from the rails console:
Charge.all.each{|c| ChargesReservation.create(charge_id: c.id, reservation_id:c.reservation_id)}
And finally change the associations to habtm associations as you have indicated in your question
# charge.rb
has_and_belongs_to_many :reservations
#reservation.rb
has_and_belongs_to_many :charges
Oh and you can delete the reservation_id column in the charges table with another migration, once you are sure everything is working correctly. This is the point where you could create a problem b/c you're destroying data, so be sure that the join table was correctly populated.
You actually don't need the join model any longer either, it was just a convenient way to populate the join table. So you can delete the charges_reservation.rb model.

Rails Converting a has_many relationship into a has and belongs to many

I have a Rails app with the following relationship:
region.rb
class Region < ActiveRecord::Base
has_many :facilities
end
facility.rb
class Facility < ActiveRecord::Base
belongs_to :region
end
I want to expand functionality a bit so that facilities can belong to more than one region at a time. I believe I can do this with a has_many_through relationship but I'm needing some guidance on converting the existing has_many into a has many through. I understand how to create and wire up the join table, but how would I take existing data and translate it?
So for instance. On a facility object there is region_id, since the facilities can belong to more than one region I'd probably need a region_ids field and shovel the collection of regions into that column which should then populate the other side of the association via the join table. I have this part pretty much figured out as far as moving forward and wiring up the association. But I'm unsure as to how to take existing data and translate it over so the app doesn't break when I change the model association.
Any advice would be greatly appreciated.
I suggest you to always use has_many :through instead of HBTM.
To establish this kind of relation you'll need the following set up:
# region.rb
class Region
has_many :facility_regions
has_many :facilities, through: :facility_regions
end
# facility.rb
class Facility
has_many :facility_regions
has_many :regions, through: :facility_regions
end
# facility_region.rb
class FacilityRegion
belongs_to :facility
belongs_to :region
end
Also, of course, you'll need to create a migration:
rails g migration create_facility_regions facility_id:integer region_id:integer
# in this migration create a uniq index:
add_index :facility_regions, %I(facility_id region_id), name: :facility_region
rake db:migrate
UPD
As to migration from one database state to another one.
I think it should not be a problem.
1) Do not delete the relations you had before (leave has_many :facilities and belongs_to :region in models).
2) When new table is created and new associations added to the classes (which I showed) create a new migration:
rails g migration migrate_database_state
3) Write the script, which will create new records in db (to reflect the current state of things):
ActiveRecord::Base.transaction do
Facility.where.not(region_id: nil).find_each do |facility|
next if FacilityRegion.find_by(falicity_id: facility.id, region_id: facility.region_id)
FacilityRegion.create!(facility_id: facility.id, region_id: facility.region_id)
end
end
4) Put this script into last created migration and run it (or in console without migration, effect would be the same).
5) After script is successfully run, create new migration in which you delete region_id from facilities table and remove these associations definitions (has_many :facilities and belongs_to :region) from models.
It must be it. I might have made some typos or so, make sure I did not miss anything and
You need to add another model, a "middle guy" called FacilityRegion.rb, like this:
facility.rb
class Facility < ActiveRecord::Base
has_many :falicity_regions
has_many :regions, through: falicity_regions
end
facility_region.rb
class FacilityRegion < ActiveRecord::Base
belongs_to :region
belongs_to :facility
end
region.rb
class Region < ActiveRecord::Base
has_many :falicity_regions
has_many :facilities, through: falicity_regions
end
If you want to use belongs_and_has_many relationship, you need to:
rails g migration CreateJoinTableRegionsFacilities regions facilities
Then,
rake db:migrate
Now, your relationships should be:
Region.rb:
class Region < ApplicationRecord
has_and_belongs_to_many :facilities
end
Facility.rb
class Facility < ApplicationRecord
has_and_belongs_to_many :regions
end
In order to populate the new join table, you will need to in your console:
Region.all.find_each do |r|
Facility.where(region_id: r.id).find_each do |f|
r.facilities << f
end
end
Now, you can either leave the columns region_id and facility_id in Facility and Region table, respectively, or you can create a migration to delete it.

Missing Attribute Error

I'm working with Rails, and when setting up some tests I encountered:
ActiveModel::MissingAttributeError:
can't write unknown attribute `group_id`
I'm guessing the issue is in my relations. I have
class Group < ActiveRecord::Base
has_many :transactions
has_many :users
end
And
class Transaction < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
And lastly,
class User < ActiveRecord::Base
belongs_to :group
has_many :transactions
end
I saw that someone had the same error because they were using has_one rather than belongs_to and needed to add an ID column to their DB. I'm using belongs_to though, so I don't think that's what I need? Any ideas?
Looks like you don't have the group_id column in your db.
You must remember that Rails is built on top of a relational database, which means that you can access "related" data by referencing a foreign_key.
When setting up a belongs_to / has_many association, the belongs_to table needs to have the appropriate foreign key (in your case group_id):
Your error doesn't state which model you're receiving the exception for; I would hazard a guess that it's User or Transaction.
--
To fix it, I would recommend creating a migration to add the group_id attribute to the appropriate model:
$ rails g migration AddGroupId
#db/migrate/add_group_id____________.rb
class AddGroupID < ActiveRecord::Migration
def change
add_column :users, :group_id, :integer
end
end
$ rake db:migrate
Unless you created the model you are referring to with a migration that had references, you will still need a migration in your database. An easy way to check if the database has one is to visit your some_project_root/db/schema.rb. If you don't see a the field you want there then you will have to generate one. The way you would do so is to run a rails g migration AddXidToY x_id:integer . It should set a field up for the id in the table you want.

Issue with polymorphic association in a subclass

I have some issues with the polymorphic associations in Rails 3. My model looks like this:
class Address < ActiveRecord::Base
belongs_to :contactable, :polymorphic => true
end
class OrganisationUnit < ActiveRecord::Base
# some other associations
end
# Subclass of OrganisationUnit
class Company < OrganisationUnit
has_one :address, :as => :contactable
end
Now, when I want to get the Address of a Company, Rails generates the following SQL-Query:
SELECT `addresses`.* FROM `addresses` WHERE (`addresses`.contactable_id = 1021 AND `addresses`.contactable_type = 'OrganisationUnit') LIMIT 1
In my opinion it's wrong, because the contactable_type should be "Company".
Is there any way I can fix this or tell rails that OrganisationUnit is just an abstract base class?
The is an expected behavior. When you link a STI table to a polymorphic association, Rails stores the base class name rather than the inherited class names. The STI type conversion happens after the object lookup by id.

Multiple has_many association through same table in Rails

I have following database schema:
I want to be able to do something like this:
dog.head << Feature.new(...)
dog.tail << Feature.new(...)
I am new to Rails, so I am not always sure by 100% what I am writing, but I tried following declaration of Dog class, and failed :) :
class Dog < ActiveRecord::Base
has_many :features, :through=>:dog_features
has_many :head_features, :through=>:dog_features, :class_name=>'Feature', :conditions=>{:group=>1}
has_many :tail_features, :through=>:dog_features, :class_name=>'Feature', :conditions=>{:group=>2}
end

Resources