I have a User and Address models. A user may have many addresses and one as default. I currently use this that works
# User.rb
belongs_to :default_address, class_name: "Address", foreign_key: :default_address_id
Now I made the Address belongs_to :addressable, polymorphic: true.
My question is how to tell this default_address self association to use the addressable instead of going directly to the Address class
Solved using has_one instead of belongs_to on User.rb
has_one :default_address, class_name: "Address", as: :addressable, dependent: :destroy
accepts_nested_attributes_for :default_address
Related
Is it possible to eager load polymorphic nested associations? How can I include doctor_profile's for Recommendation's and patient_profile's for Post's?
I'm able to call Activity.includes(:trackable).last(10) but not sure how to include the associated models past there. I've tried belongs_to :recommendation, -> { includes :patient_profile, :doctor_profile} with no luck
class Activity
belongs_to :trackable, polymorphic: true
end
class Recommendation
has_many :activities, as: :trackable
belongs_to :doctor_profile
end
class Post
has_many :activities, as: :trackable
belongs_to :patient_profile
end
with respect referenced this SO answer and comments
for your problem you can managed with foreign_type field from polymorphic table to reference which model that use it
class Activity
belongs_to :trackable, polymorphic: true
# below is additional info
belongs_to :recommendation, foreign_type: 'Recommendation', foreign_key: 'trackable_id'
belongs_to :post, foreign_type: 'Post', foreign_key: 'trackable_id'
end
and you can call it
Activity.includes(recommendation: :doctor_profile).last(10)
Activity.includes(post: :patient_profile).last(10)
Activity.includes(recommendation: :doctor_profile) means
Activity will join recommendation with foreign_type and trackable_id
and then from recommendation will join doctor_profile with doctor_profile_id
The above answer works, but the use of foreign_type isn't actually supposed to do what the commenter intended.
https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
foreign_type is used to specify the name for the column which determines the class type for the relation.
I think the intended result here is to instead use class_name to specify which table the relation is referring to. If the relation has the same name as the table, then class_name can actually be inferred (which is why the provided answer works in the first place)
In order to get the above answer to work, specifying inverse_of for the belongs_to and adding for the has_many associations got everything to work. For example:
class Activity
belongs_to :trackable, polymorphic: true
# below is additional info
belongs_to :recommendation, foreign_type: 'Recommendation', foreign_key: 'trackable_id', inverse_of: :activities
belongs_to :post, foreign_type: 'Post', foreign_key: 'trackable_id', inverse_of: :activities
end
On the Post model:
has_many :activities, inverse_of: :post
On the Recommendation model:
has_many :activities, inverse_of: :recommendation
I have the following rails models:
# Address Model
address_line_1
address_line_2
address_line_3
post_code
country
# Vendor model
name
registered_address_id
billing_address_id
display_address_id
registered_address, billing_address and display_address should refer to an address in the address table.
so eventually I can do Vendor.registered_address etc.
I am confused about how to go about this with regards to migrations and my relationships on my model, can anyone point me in the right direction?
Many thanks
You can do it declaring explicitly class_name on belongs_to and has_many and foreign_key on has_many
class Vendor < ApplicationModel
belongs_to :registered_address, class_name: 'Address'
belongs_to :billing_address, class_name: 'Address'
belongs_to :display_address, class_name: 'Address'
end
class Address < ApplicationModel
has_many :registered_addresses, foreign_key: :registered_address_id, class_name: 'Vendor'
has_many :billing_addresses, foreign_key: :billing_address_id, class_name: 'Vendor'
has_many :display_addresses, foreign_key: :billing_address_id, class_name: 'Vendor'
end
References: -
has_many
belongs_to
Query =>
vendor_object.registered_address
vendor_object.billing_address
vendor_object.display_address
I have one user model and one viewed_contractor model. I am treating user model as customer and contractor. customer can view many contractors by visiting their respective profile.Contractor can be viewed by many customers. I have customer_id and contractor_id in my viewed_contractor. I want to handle this relation as has_many through. Is it possible thorough has_many through?
It is possible. First, you'd need to specify the class_name option for the belongs_to associations in your ViewedContractor model so that they both refer to your User class. Then you could specify the has_many through: relations in your User model.
Something like this should work:
# viewed_contractor.rb
class ViewedContractor < ActiveRecord::Base
belongs_to :contractor, class_name: 'User', foreign_key: :contractor_id
belongs_to :customer, class_name: 'User', foreign_key: :customer_id
end
# user.rb
class User < ActiveRecord::Base
has_many :viewed_contractors_as_contractor, class_name: 'ViewedContractor', foreign_key: :contractor_id
has_many :viewed_contractors_as_customer, class_name: 'ViewedContractor', foreign_key: :customer_id
has_many :visited_contractors, through: :viewed_contractors_as_customer, source: :contractor
has_many :visited_customers, through: :viewed_contractors_as_contractor, source: :customer
end
Assume we have this relationship:
class Company < ActiveRecord::Base
has_one :company_address, class_name: 'Address', foreign_key: 'company_address_id'
has_one :overseas_address, class_name: 'Address', foreign_key: 'overseas_address_id'
end
If I pry within a new Address instance, is it possible to return :company_address or :overseas_address?
Fields are the same, but I want to do different validations.
Yes, there are alternative ways of solving the problem, but I'm more interested into whether finding the name of the relationship is actually possible.
Thanks!
You should have corresponding belong_to: in your Address model, then try Address.reflect_on_all_associations(belongs_to)
it will return a collection of ActiveRecord::Reflection::AssociationReflection which will have different name (depending on your belong_to)
In order to check instance you should mark your belong_tos as inverse_of: :put_corresponding_association, your has_one as inverse_of belong_tos and then you can check corresponding methods (generated by belong_tos) for presence of parent Company object
The idea is assume an Address as
class Address < ActiveRecord::Base
belongs_to :company, class_name: 'Company', foreign_key: 'company_address_id', inverse_of: :company_address
belongs_to :oversea, class_name: 'Company', foreign_key: 'overseas_address_id', inverse_of: :overseas_address
end
class Company < ActiveRecord::Base
has_one :company_address, class_name: 'Address', foreign_key: 'company_address_id', inverse_of: :company
has_one :overseas_address, class_name: 'Address', foreign_key: 'overseas_address_id', inverse_of: :oversea
end
and you create address as address = company.overseas_address.new
then
address.company == nil
address.oversea == company
talk_groups table
field :parent_topic_id, type: Integer, default: -1
has_many :topics, :dependent => :destroy
belongs_to :parent_topic, :class_name => 'Topic', :foreign_key => :parent_topic_id
topics table
belongs_to :talk_group
The relation above works well on sqlite/mysql, but doesn't work on mongoid
because when a model can't have many and belongs_to with another same model
parent_topic.talk_group will appear Mongoid::Errors::AmbiguousRelationship: error
the problem is caused not because "when a model can't have many and belongs_to with another same model".
it's because when mongoid looks at the topic model, it can't know if the talk_group field is referring to the topics or parent_topic relation in the talk_group model.
mongoid provided an option to avoid this "AmbiguousRelationship" it's the inverse_of option...
so to solve this issue you should alter the topic model to become
belongs_to :talk_group, inverse_of: :parent_topic
and the talk_group model should become
has_many :topics, dependent: :destroy
belongs_to :parent_topic, class_name: 'Topic'
# you can add inverse_of option on this side as well although it's not mandatory
belongs_to :parent_topic, class_name: 'Topic', inverse_of: :talk_group
also refere to this question for more info.