Dynamic has_many class_name using polymorphic reference - ruby-on-rails

I am trying to associate a polymorphic model (in this case Product) to a dynamic class name (either StoreOnePurchase or StoreTwoPurchase) based on the store_type polymorphic reference column on the products table.
class Product < ActiveRecord::Base
belongs_to :store, polymorphic: true
has_many :purchases, class_name: (StoreOnePurchase|StoreTwoPurchase)
end
class StoreOne < ActiveRecord::Base
has_many :products, as: :store
has_many :purchases, through: :products
end
class StoreOnePurchase < ActiveRecord::Base
belongs_to :product
end
class StoreTwo < ActiveRecord::Base
has_many :products, as: :store
has_many :purchases, through: :products
end
class StoreTwoPurchase < ActiveRecord::Base
belongs_to :product
end
StoreOnePurchase and StoreTwoPurchase have to be separate models because they contain very different table structure, as does StoreOne and StoreTwo.
I am aware that introducing a HABTM relationship could solve this like this:
class ProductPurchase < ActiveRecord::Base
belongs_to :product
belongs_to :purchase, polymorphic: true
end
class Product < ActiveRecord::Base
belongs_to :store, polymorphic: true
has_many :product_purchases
end
class StoreOnePurchase < ActiveRecord::Base
has_one :product_purchase, as: :purchase
delegate :product, to: :product_purchase
end
However I am interested to see if it is possible without an extra table?

Very interesting question. But, unfortunately, it is impossible without an extra table, because there is no polymorphic has_many association. Rails won't be able to determine type of the Product.purchases (has_many) dynamically the same way it does it for Product.store (belongs_to). Because there's no purchases_type column in Product and no support of any dynamically-resolved association types in has_many. You can do some trick like the following:
class Product < ActiveRecord::Base
class DynamicStoreClass
def to_s
#return 'StoreOnePurchase' or 'StoreTwoPurchase'
end
end
belongs_to :store, polymorphic: true
has_many :purchases, class_name: DynamicStoreClass
end
It will not throw an error, but it is useless, since it will call DynamicStoreClass.to_s only once, before instantiating the products.
You can also override ActiveRecord::Associations::association to support polymorphic types in your class, but it is reinventing the Rails.
I would rather change the database schema.

Related

How to create a group of users (roomates) within one product (property) in Rails

I have a question on a platform I'm developing in Ruby on Rails 5.2.
I have an Owner model which is the owner of properties/property. The owner will post a property so that users (in this case roomates) can share the same property/house/department, etc.
I have Owners and I have Users (both tables are created using devise):
Owner.rb:
class Owner < ApplicationRecord
has_many :properties
end
User.rb:
class User < ApplicationRecord
#Theres nothing here (yet)
end
This is where the magic happens. Property.rb:
class Property < ApplicationRecord
belongs_to :owner
has_many :amenities
has_many :services
accepts_nested_attributes_for :amenities
accepts_nested_attributes_for :services
mount_uploaders :pictures, PropertypictureUploader
validates :amenities, :services, presence: true
scope :latest, -> { order created_at: :desc }
end
How can multiple users share a property? I'm aware that it will have a many-to-many association but I'm a bit confused how to connect these relationships so when the owner posts a property it will display something like:
Property available for: 3 users
And then begin to limit users until it completes the amount of users available.
This sounds like your average many to many assocation:
class User < ApplicationRecord
has_many :tenancies, foreign_key: :tenant_id
has_many :properties, through: :tenancies
end
class Tenancy < ApplicationRecord
belongs_to :tenant, class_name: 'User'
belongs_to :property
end
class Property < ApplicationRecord
has_many :tenancies
has_many :tenants, through: :tenancies
def availablity
# or whatever attribute you have that defines the maximum number
max_tenants - tenancies.count
end
end
You can restrict the number of tenants with a custom validation.
You can use a join table, called users_properties. This table will have a property_id and user_id. You'll then have the following in your properties model:
has_many :users_properties
has_many :users, through: :users_properties
Read more about it here https://guides.rubyonrails.org/association_basics.html

has_many :through and join table in Rails

I would like to implement properly database and associated models for my application.
I have two models: user and location. I would like them to be joined by a table due to the fact that I need to keep historic data about the association between these two entities.
Therefore, I created a join table called user_locations in addition to the foreign keys of location_id and user_id I have two additional fields which I need.
So far so good.
A new requirement has emerged and I need my location to be polymorphic.
I don't know how to set up my models properly to store to be able to have that joined table and a polymorphic association.
Here is what I came up with so far:
user.rb
class User < ApplicationRecord
has_many :locations, through: :user_locations, as: :locationable, source_type: 'User'
has_many :user_locations
end
location.rb
class Location < ApplicationRecord
belongs_to :locationable, polymorphic: true
has_many :users
end
user_location.rb
class UserLocation < ApplicationRecord
belongs_to :user
belongs_to :location
validates_presence_of :user
validates_presence_of :location
end
I found a very good tutorial/article that I think can help you here.
It would entail you doing something like:
class User < ApplicationRecord
has_many :locations
has_many :model_ones, through: :locations, source: :locationable, source_type: 'ModelOne'
has_many :model_twos, through: :locations, source: :locationable, source_type: 'ModelTwo'
And:
class Location < ApplicationRecord
belongs_to :locationable, polymorphic: true
belongs_to :user
end
Where ModelOne and ModelTwo are filled in with the models you need obviously.

Rails has_many association through a belongs_to polymorphic association

In my database there are 4 models
class MasterPayment < ActiveRecord::Base
has_many :payments
end
class Payment < ActiveRecord::Base
belongs_to :master_payment
belongs_to :payable, polymorphic: :true
end
class TreatmentPlan < ActiveRecord::Base
has_many :payments, as: :payable
end
class ArbitraryBillableItem < ActiveRecord::Base
has_many :payments, as: :payable
end
What i would like to do is set up an association in MasterPayment that will associate payables to master payments.
Currently, the closest i could find was setting up the master payment model as follows
class MasterPayment < ActiveRecord::Base
has_many :payments
has_many :treatment_plans, through: :payments, source: :payable, source_type: "TreatmentPlan"
has_many :arbitrary_billable_items, through: :payments, source: :payable, source_type: "ArbitraryBillableItem"
def payables
self.treatment_plans + self.arbitrary_billable_items
end
end
The only problem i have with this is that it doesn't feel like the "correct" way to do it.
The only reason i can see for rails not having a solution to this is because you would presumably have to union the tables to return it in one sql statement.
Is there an alternative way to accomplish this that will make more use of the active record associations?
This seems too simple but would it work?
def payables
self.payments.joins(:treatment_plans, :arbitrary_billable_items).distinct
end

Rails activerecord deep eager loading

Here is my models:
class User < ActiveRecord::Base
has_many :products
has_many :comments
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
I need to get comment records from current user products only
How do I do that? thanks
If we move the relationships to use a has_many: comments, through: products you can probably get what you're after:
class User < ActiveRecord::Base
has_many :products
has_many :comments, through: products
end
class Product < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :product
end
Now you can do user.comments.
The rails docs are here, which say:
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model. For example, consider a
medical practice where patients make appointments to see physicians.

Active record association works only in one way

I've a problem with an active record relation. I have a Content class and a Product Class defined in this way:
Class Content < ActiveRecord::Base
has_many :product_relations, as: :productable
has_many :products, through: :product_relations
end
Class Product < ActiveRecord::Base
end
Content.first.products return me the products associated to the contents, but I cannot create an association to build a query like Product.first.contents that return me all the contents associated to a product.
Update
I have also this class:
class ProductRelation < ActiveRecord::Base
belongs_to :product
belongs_to :productable, polymorphic: true
end
Solved
has_many :contents, through: :product_relations, source_type: "Content", source: :productable

Resources