Polymorphic relation with inherited model - ruby-on-rails

I have used polymorphic relation in one of my task having inherited models like
Class Organization < ActiveRecord::Base
end
Class Company < Organization
has_many :roles, as: :authorizable
end
Class User < ActiveRecord::Base
has_many :roles, as: :authorizable
end
Class Role < ActiveRecord::Base
belongs_to :authorizable, :polymorphic => true
end
When I try to fetch User.find(user_id).roles, it gives me correct result; however, when I try to query like Company.find(company_id).roles, it gives me blank array as it queries like
SELECT * FROM `roles` WHERE (`roles`.authorizable_id = 5 AND `roles`.authorizable_type = 'Organization')
It should be like 'authorizable_type' = 'Company'
What am I doing wrong?

I don't think you are doing anything wrong. The code seems to be using single table inheritance. Organization and its subclasses share the same database table, but are distinguished by the value of the type column (for Company the type will be "Company").
In a polymorphic relation with single table inheritance, the authorizable_type will always be the name of the parent model ("Organization").
See here.

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

Sort a Rails model by an attribute on its polymorphic associations

I imagine this is a duplicate question but I haven't found it yet so I figured I'd ask. I have a User model with different types of Profile models through a polymorphic association. Something like:
class User < ActiveRecord::Base
belongs_to :profile, polymorphic: true
end
class Profile < ActiveRecord::Base
has_one :user, as: :profile
end
class FacebookProfile < ActiveRecord::Base
has_one :user, as: :profile
end
I'd like to perform a query on User that returns Users sorted by the first name of their profile object. Before when there wasn't any polymorphic relationship I could do this by starting with a joins on :profile, but I know that won't work with polymorphism.
Is there a better way to do this besides using sort or sort_by like:
User.all.sort_by {|user| user.profile.first_name }
It's doable with SQL.
SELECT users.*, COALESCE(profiles.first_name, facebook_profiles.first_name) AS c_first_name FROM users
LEFT JOIN profiles ON users.profile_id=profiles.id AND users.profile_type="Profile"
LEFT JOIN facebook_profiles ON users.profile_id=facebook_profiles.id AND users.profile_type="FacebookProfile"
ORDER BY c_first_name ASC
You should be able to create a scope with this "manual" join on the User model, in that you can use COALESCE on the two columns to create an alias that you can sort on.
Try something like this
class User < ActiveRecord::Base
belongs_to :profile, polymorphic: true
scope :sort_by_first_name, -> { select("users.*", "COALESCE(profiles.first_name, facebook_profiles.first_name) AS c_first_name").joins("LEFT JOIN profiles ON users.profile_id=profiles.id AND users.profile_type='Profile'").joins("LEFT JOIN facebook_profiles ON users.profile_id=facebook_profiles.id AND users.profile_type='FacebookProfile'").order("c_first_name ASC") }
end
I haven't tried this myself but in theory it should work.

Accesing attributes in the joining table with has_many through

I have a many2many relationship with a has_many through association:
class User < ActiveRecord::Base
has_many :trips_users
has_many :trips, through: :trips_users
end
class Trip < ActiveRecord::Base
has_many :trips_users
has_many :users, through: :trips_users
end
class TripsUser < ActiveRecord::Base
belongs_to :user
belongs_to :trip
end
The joining table trips_user contains a column named 'pending' which id like get when I ask for a list of trips of a user.
So in my controller I need to get all trips a user has, but also adding the 'pending' column.
I was trying
current_user.trips.includes(:trips_users)
that will be done by this select statement:
SELECT trips.* FROM trips INNER JOIN trips_users ON trips.id
= trips_users.trip_id WHERE trips_users.user_id = 3
which is missing the information in the trips_users table that I want.
The desired sql would be:
SELECT trips.*, trips_users.* FROM trips INNER JOIN trips_usersON trips.id =
trips_users.trip_id WHERE trips_users.user_id = 3
This finally worked:
current_user.trips.select('trips_users.*, trips.*')
Overriding the select part of the SQL.
Not very pretty in my opinion thou, I shouldn't be messing with tables and queries but models, specially in such a common case of a m2m association with extra data in the middle.
You'll want to use joins rather than includes for this... See the following Rails Guide:
http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Essentially you'd do something like this:
current_user.trips.joins(:trips_users)
The includes method is used for eager loading, while joins actually performs the table join.
You could also try:
trips_users = current_user.trips_users.includes(:trip)
trips_users.first.pending?
trips_users.first.trip
Which should give you the trips_users records for that user but also eager loading the trips so that accessing them wouldn't hit the database again.

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.

Resources