Access has_many :through with conditions - ruby-on-rails

I have model BatchClass and SubjectTeacherBatchClass having attribues like below.
class SubjectTeacherBatchClass < ApplicationRecord
belongs_to :batch_class
belongs_to :user, optional: true
belongs_to :shift
belongs_to :session_batch
end
and
class BatchClass < ApplicationRecord
has_many :subject_teacher_batch_classes
has_many :subjects, through: :subject_teacher_batch_classes
has_many :teachers, source: :user, foreign_key: 'user_id', through: :subject_teacher_batch_classes
end
I want to access batch_class.teachers but with custom attributes like i.e.
batch_class.teachers.where(session_batch_id: session_batch.id, shift_id: current_shift.id)
The problem is that session_batch_id and shift_id are not attributes of User class (alias teacher) but are attributes of SubjectTeacherBatchClass class. (the through class)
Is there any better way to get teachers from batch_class passing those default attributes (session_batch and shift)

Looks like https://stackoverflow.com/q/18799934/1154044 question rather similar to yours.
According to your models, you can try this query
batch_class.teachers.
references(:subject_teacher_batch_classes).
where(subject_teacher_batch_classes: {
session_batch_id: session_batch.id, shift_id: current_shift.id
})

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.

has_many (different objects) as: one type of object

I need a way of referrencing 2 different objects as 1.
I have a Message object with needs to keep track of Recipients. the problem is that Recipients could be a User or a Contact.
should the models be: ?
class Message < ActiveRecord::Base
has_many :users, as: :recipients
has_many :contacts, as: :recipients
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
class User < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
end
because, I feel like polymorphic relationships are built to go the opposite way.
also, this way doesn't allow me to reference #message.recipients which is what I need.
I hope this makes sense
Thank you
What you have done is completely incorrect. I think you need many-to-many association. My association whould be that:
class Message < ActiveRecord::Base
has_many :recipient_links
has_many :users, through: :recipient_links, source: :recipient, source_type: 'User'
has_many :contacts, through: :recipient_links, source: :recipient, source_type: 'Contact'
end
class Contact < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
class User < ActiveRecord::Base
has_many :recipient_links, as: :recipient
has_many :messages, through: :recipient_links
end
# fields: message_id, recipient_id, recipient_type
class RecipientLink < ActiveRecord::Base
belongs_to :recipient, polymorphic: true
belongs_to :message
end
... I can't add comments yet so i give here solution for: When use answer from up to receive all #message.recipients every type in only 2 request:
RecipientLink.includes(:recipient).where(message_id: #message.id).collect(&:recipient)

Dynamic has_many class_name using polymorphic reference

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.

has_many :through and has_many relationship between same 2 models

A list has one owner (a user). A list also has a number of panelists (also users). I have tried defining the relationships between the three models: User, List, and Panelist. But I'm getting nowhere.
user.rb
class User < ActiveRecord::Base
has_many :lists
has_many :panelMemberships, :through => :panelists, :source => :lists
end
list.rb
class List < ActiveRecord::Base
belongs_to :user
has_many :panelMembers, :through => :panelists, :source => :user
end
panelist.rb
class Panelist < ActiveRecord::Base
belongs_to :list
belongs_to :user
end
I've tried all different combinations but nothing seems to work. Thanks in advance for any help you can provide.
The model also has to have a has_many relationship for whatever the through model is, so wherever you have has_many :x, through: :y, you also need to say has_many :y. You also shouldn't have a panelist model separate from your user model if panelists are users (unless you're doing STI, which you're not). From what I understand, you're trying to do something like this:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_and_belongs_to_many :lists # for the normal panelist / list relationship
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_and_belongs_to_many :users
end
Then you'll need to make a migration for a users_lists (with user id and list id) table which will be your join table but won't need its own model. But if you really want to keep the through relationship (good for if you do other stuff with the join model), then you'd do:
class User < ActiveRecord::Base
has_many :owned_lists, class_name: "List", foreign_key: :owner_id # this is for the owner/list relationship
has_many :panel_memberships
has_many :lists, through: :panel_memberships
end
class List < ActiveRecord::Base
belongs_to :owner, class_name: "User"
has_many :panel_memberships
has_many :users, through: :panel_memberships
end
class PanelMembership < ActiveRecord::Base
belongs_to :user
belongs_to :list

Resources