Ruby on Rails Active Record Join - ruby-on-rails

I need to joins this two tables but I cant seems to get it right.
Model
class Registration < ActiveRecord::Base
has_many :region
end
class Region < ActiveRecord::Base
belongs_to :registration
end
what I have tried so far is this
`test = Region.joins(:registration)`
which will give me this result
[#<Region id: 1, name: "region1">, #<Region id: 1, name: "region1">]
It seem that i have certainly did join the two tables but my dilemma is I also need the index that belongs to Registration Model. the about results are only the index available in the Region Model.
I also tried it the other way around
test = Registration.joins(:region)
but it gives me a error
Unknown column 'regions.registration_id' in 'on clause': SELECTregistrations.* FROMregistrationsINNER JOINregionsONregions.registration_id=registrations.id``
and I do agree with this error cause i do not have a index registration_id

when you define belongs_to relationship in a table that table should have foreign_key of table to which it is belonging. So in your example if you have relation like Region belongs to registrations then regions should have registration_id. It looks like you have defined a wrong relationship.
So if you will define registration_id in Region and execute following query:
Region.joins(:registration)
You will see id of region table along with registration_id in the result
Hope this helps

Change the Registration model code
class Registration < ActiveRecord::Base
has_many :regions
end
Then you can use joins
test = Registration.joins(:region)
Hope it helps!

class Registration < ActiveRecord::Base
has_many :regions, inverse_of: :registration, dependent: :destroy
end
class Region < ActiveRecord::Base
belongs_to :registration, inverse_of: :regions
end
Then, make sure your Region table correctly shows its association to Registration with a registration_id column. If it doesn't contain the column, go ahead and create the following migration:
rails g migration add_registration_ref_to_regions registration:references
rake db:migrate
Now, you should be able to successfully query via:
Registration.joins(:regions) or Region.joins(:registration)
Cheers!

Related

Create Rails scope comparing fields on two tables

I have a number of associated tables in an application
class Listing < ActiveRecord::Base
belongs_to :house
belongs_to :multiple_listing_service
end
class House < ActiveRecord::Base
has_one :zip_code
has_one :primary_mls, through: :zip_code
end
I wanted to create a scope that produces all the Listings that are related to the Primary MLS for the associated House. Put another way, the scope should produce all the Listings where the multiple_listing_service_id = primary_mls.id for the associated house.
I've tried dozens of nested joins scopes, and none seem to work. At best they just return all the Listings, and normally they fail out.
Any ideas?
If I understand correctly, I'm not sure a pure scope would be the way to go. Assuming you have:
class MultipleListingService < ActiveRecord::Base
has_many :listings
has_many :zip_codes
end
I would go for something like:
class House < ActiveRecord::Base
...
def associated_listings
primary_mls.listings
end
end
Update 1
If your goal is to just get the primary listing then I would add an is_primary field to the Listing. This would be the most efficient. The alternative is a 3 table join which can work but is hard to optimize well:
class Listing < ActiveRecord::Base
...
scope :primary, -> { joins(:houses => [:zip_codes])
.where('zip_codes.multiple_listing_service_id = listings.multiple_listing_service_id') }

Rails belongs_to association with multiple foreign keys

I'm trying to figure out a way to define a belongs_to association where the target record can be found by one of 4 different columns. The setup:
User
class User < ActiveRecord::Base
has_many :managerships, foreign_key: :employee_id
has_many :managers, through: :managerships
end
Managership
class Managership < ActiveRecord::Base
belongs_to :employee, class_name: 'User'
belongs_to :manager # <-- ideas?
end
The managerships table has 4 columns manager_id, manager_custom_id, manager_email, manager_saml_id that we can use to look up the user's managers. For every Managership, only one of these columns is ever present.
I'm looking for a way to be able to call user.managers (a manager is a User) so that it returns all users where managerships.manager_id = users.id OR managerships.manager_custom_id = users.custom_id OR managerships.manager_email = users.email OR managerships.manager_saml_id = users.saml_id
Many thanks for any ideas!
Although, I would doubt that 4 foreign keys for one relation is a good idea at all, one way to get managers from User is to use method instead of scope. However you won't be able to use #joins, if you do so.
class User
def managers
self.class.joins(:managerships).where('managerships.manager_id = ? OR managerships.manager_custom_id = ? OR managerships.manager_email = ? OR managerships.manager_saml_id', id, custom_id, emd, saml_id)
end
end
in Managership you can do similar thing, declare #manger instead of belongs_to :manager

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.

Rails active record query with multiple associations

I have tables called users, orders, and delivery_times that are linked using the following relationship.
For table User:
belongs_to :orders
For table orders:
belongs_to :delivery_times
I want to write a query on table users using a condition on table delivery_times as shown:
User.includes(order: :delivery_time).where("delivery_times.start < ?",Time.now)
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "delivery_times"
However I get an error. Can I use the RoR ORM to make this query work using includes, even though I know there is a solution using joins?
You will need a join for this kind of query, since you need the joint knowledge of the delivery_times table and the users table.
What includes actually does is it decides between preload and eager_load automatically and tries to always take the better one. In you case it will do an eager_load; have a look into this article.
For the error you get, I guess it yould result from starting with Users and not User:
User.includes(order: :delivery_time).where("delivery_times.start < ?",Time.now)
Everything else seems correct to me.
The better definition of relations between your models would be this:
So your classes would look like this:
class User < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :user
has_one :delivery_time
end
class DeliveryTime < ActiveRecord::Base
belongs_to :order
end
The query you are making doesn't make any sense? What is the result that you are expecting?
If you want to get order that their delivery time is a specific time you can use scopes:
class Order < ActiveRecord::Base
belongs_to :user
has_one :delivery_time
scope :ready_to_deliver, includes(:delivery_time).where("delivery_time.start < ? ", Time.now)
end
Then you can get orders that are ready to deliver like this:
ready_orders = Order.ready_to_deliver

how to achieve foreign key concepts in rails using association?(new to rails)

I got a trouble in my rails project to achieve foreign key concepts, I had gone through http://guides.rubyonrails.org/association_basics.html and few of tutorials and came to know that this can be achieved by association and I had tried using the same but yet to succeed.
Actually I am new to Rails and I continue running into the same path. Any guidance much appreciated...
As per my scenario , I am having two models User and Region
In User model there are three fields user_id, user_name, user_region_id (foreign key)
In Region model => region_id(primary key), region name
I want to display In user_id user_name region_name (using foreign key) on index page of of user
so how an achieve this ??Please help me out
your help will be greatly appreciated
thanks in advance for your help
Conventionally you should not prefix your column names with user_ and region_, because they are already present in the table named like that. Removing the prefix you can achieve your desired result using following code in your models:
class User < ActiveRecord::Base
belongs_to :region
end
class Region < ActiveRecord::Base
has_many :users
end
I think the easiest way to remember association in rails is that the model which has the foreign key should have the belongs_to declaration. So in your setup, since a Region has many users, it would make sense to have the foreign key in the users table.
Assuming that the foreign key in the users table is called region_id, you should declare the following association in the User model.
class User < ActiveRecord::Base
belongs_to :region
end
So this works because rails assumes that the foreign key is under a column called region_id. AND the primary key is under a column called id in the regions table.
On the side of the association, that's when you decide if it's a has_one or has_many. Since it has already been decided that it should be has_many, it's as easy as just declaring that association in the Region model
class Region < ActiveRecord::Base
has_many :users
end
If you want to keep using region_id as the primary key for the regions table, change the association in the user model to
class User < ActiveRecord::Base
belongs_to :region, primary_key: :region_id
end
AND make sure to declare it in the region model so that doing region.users will work
class Region < ActiveRecord::Base
self.primary_key = 'region_id'
end

Resources