How to set up a polymorphic has_many :through? - ruby-on-rails

I'm having trouble figuring out the right way to set this association up.
I have 3 models: Musicians, Bands and Managers. Each of those can have a certain "level" associated with them (stress, happiness, etc).
But I'm not sure how to correctly associated my levels with the other 3.
There needs to be an intermediary model that connects the levels with the different items and sets what the current level is at.
Do I need some sort of has_many :through that's polymorphic? And how on earth do I set that up? Is there some other type of associated I need?
I asked a similar question before but did a thoroughly awful job of explaining what I was trying to do, so this is my second attempt.

ItemLevels needs a type to know what it is associated with:
class ItemLevel < ActiveRecord::Base
# levelable_id :integer
# levelable_type :string(255)
belongs_to :levelable, :polymorphic => true
has_many :levels
end
class Musician < ActiveRecord::Base
has_many :levelables
end
class Musician < ActiveRecord::Base
has_many :levelables
end
class Manager < ActiveRecord::Base
has_many :levelables
end
Level then only needs to know what ItemLevel it is associated with:
class Level < ActiveRecord::Base
belongs_to :item_level
end
If you want to be strict, you should put some validation in you Level model to check it has an ItemLevel and the type being instantiated.

Related

ActiveRecord association through multiple tables

I have the following schema for my Rails app :
A project has many reviews, each of those reviews is filled out by a unique User to calculate a global score.
We could say that "An Organization of Users handles many Entities that have many Projects which are reviewed by the Users".
As you can see, I have a circular reference since I linked the tables "Users" & "Reviews".
After many tries and many search, I just can't manage to associate a User to a Review...Here is what I did so far:
1. Creation of an association table "UserReviews"
2. Model User
class User < ApplicationRecord
belongs_to :organization
####
has_many :user_reviews
has_many :reviews, through: :user_reviews
end
3. Model Review
class Review < ApplicationRecord
belongs_to :project
##
belongs_to :user_reviews
has_one :user, through: :user_reviews
end
4. Model UserReview
class UserReview < ApplicationRecord
belongs_to :user
belongs_to :review
end
I want to be able to get user.reviews or review.user.
Basically...I have to admit I'm lost despite the documentation. I never had to deal with this kind of issue.
Many thanks for your help!
Why do you need UserReview model here? I suppose Review model suffices your use case.
Change the Review model to:
class Review < ApplicationRecord
belongs_to :project
belongs_to :user
end

Active Record Associations- One Model can belong to different models at the same time

What is the correct way to create active record relationships for a scenario where-
One instance of a Model A can belong to multiple of instances of two other models, Model B and Model C at the same time.
Model B can only have one Model A.
Model C can have many Model A's.
An example is a job board where an employer is hiring for one position and candidate is applying and has many previous positions. I want both the candidate and employer positions to be from the same model.
Position Mode (Model A)
can belong to both employers and candidates at the same time
Employer Model (Model B)
has_one position
Candidate Model (Model C)
has_many positions
Here's one way of accomplishing what you described in the question:
class Position < ActiveRecord::Base
belongs_to :candidate
belongs_to :employer
end
class Employer < ActiveRecord::Base
has_many :positions
end
class Candidate < ActiveRecord::Base
has_many :positions
ens
Note that a position can only belong to one employer and one candidate at a time; this is kind of nonsensical if you are creating a job board - the relationship between position and employer is probably correct, but candidates might have many applications, and an application would belong_to a position, while a position has_many applications. Or possibly also "favorites" so candidates could keep track of a list of positions to apply to.
The "theoretical" example that you started with:
One instance of a Model A can belong to multiple of instances of two other models, Model B and Model C at the same time.
Model B can only have one Model A.
Model C can have many Model A's.
Code:
class ModelA < ActiveRecord::Base
has_many :model_bs
has_many :model_c_relationships # join table!
has_many :model_cs, through: :model_c_relationships
end
class ModelB < ActiveRecord::Base
belongs_to :model_a
end
class ModelCRelationship < ActiveRecord::Base
belongs_to :model_a
belongs_to :model_c
end
class ModelC < ActiveRecord::Base
has_many :model_c_relationships
has_many :model_as, through: :model_c_relationships
end
If there is a situation where one type of object can belong to one other object, but that other object can be one of several types, then it might need a polymorphic association. For example, StackOverflow allows you to comment on both questions and answers. A comment can only belong to one comment-able parent object, therefore the code might look like this:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
class Question < ActiveRecord::Base
has_many :comments, as: :commentable
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
has_many :comments, as: :commentable
end

Modelling Many Rails Associations

I'm trying to wrap my head around how I should model my database for a parent model with many has_one associations (20+). I have one model called House which is the parent model of all the other models:
class House < ActiveRecord::Base
has_one :kitchen
has_one :basement
has_one :garage
has_one :common_room
#... Many other child models
end
All the child models contain unique properties specific to their own class. I've thought about STI, but there isn't really any shared functionality or inputs that I can use across models. I've also thought of making one 'super model', but that doesn't really follow Rails best practices, plus it would contain over 200 columns. Is there another design pattern or structure that I can use to model this efficiently so that I can reduce database calls?
class House
has_many :rooms
Room::TYPES.each do |room_type|
has_one room_type, -> { where(room_type: room_type) }, class_name: "Room"
end
class Room
belongs_to :house
TYPES = %i/kitchen basement garage common_room etc/.freeze
end
In your migration make sure to add_index :rooms, [:type, :house_id], unique: true. Unless a house can have more than 1 type of room. If that is the case, I think a different approach is needed.
To your second question, it depends really, what type of database are you using? If its PostgreSQL you could use hstore and store those as a properties hash. Or you could serialize you db to take that as a hash. Or you could have another model that room has many of. For instance has_many :properties and make a property model that would store that information. Really depends on what else you want to do with the information
Why not using Polymorphism in Rails? It would be much simpler
class House < ActiveRecord::Base
has_many :properties, as: :property_house
end
class Kitchen < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
class Garage < ActiveRecord::Base
belongs_to :property_house, polymorphic: true
end
For more information, go here: http://terenceponce.com/blog/2012/03/02/polymorphic-associations-in-rails-32/

Planning a Rails application associations

I'm in the process of trying to develop my first rails application and before I jump off into actually implementing my ideas, I was hoping I could get some help with my association planning.
My application is going to be an educational tool and I have some basic functionality that I need to implement. I want to have students that can register for and create courses. Within these courses, there will be one user who is the teacher and the rest are students. Within the course will be assignments that the teacher creates and that users are required to make a submission for. These are the basics. Eventually I want to add more functionality but as of now I just need the foundations set.
Here are my ideas so far on what the associations should look like:
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
However, I'm running into my wall of inexperience at the moment with how to appropriately handle the rest of the associations at this point. I could probably hack out something, but I'd prefer to do it as best as I can the first time.
How do I handle the associations related to assignments and submissions? Presumably a student's submission should belong_to them, but it is also specifically related to an assignment within the class. Assignments originate within a course, but are also closely tied to the user.
Finally, what's the best way to handle the relationships between a user and the class they create? A user creates a course. Does that course belong_to them? Should I just add a column to the course's table for storing the creator's id? Etc.
Thanks for the help.
Suggestion:
You might want to separate out your Teacher and Student models, since you're very likely to have different actions associated with each (and while they share some attributes, they really are different entities in your model, for example, you likely want just one teacher teaching in a course.)
You could derive both the Teacher model and the Student model from a User model that has the shared attributes and authentication.
Now to your questions:
If you'd like to keep the student that created the course associated, creator_id is the way I'd go. (If a teacher can create a course too, deriving Student and Teacher from a shared User model would help)
Assignments and Submissions:
You've pretty much defined it in words. Here is the code.
[If different students within a course could get different assignments, only then do you want to build a direct association between Student and Assignment, otherwise, use a through association]
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
has_many :assignments, :through => :courses
has_many :submissions
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
has_many :assignments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
class Assignment < ActiveRecord::Base
belongs_to :course
has_many :submissions
end
class Submission < ActiveRecord::Base
belongs_to :assignment
belongs_to :user
end

Proper Rails Association to use

I am trying to create an association between two tables. A student table and a computer table.
A computer can only ever be assigned to one student (at any one time) but a student can be assigned to multiple computers.
This is what I currently have in mind. Setting up a has-many through relationship and modifying it a bit.
class Student < ActiveRecord::Base
has_many :assignemnts
has_many :computers, :through => :assignments
end
class Computer < ActiveRecord::Base
has_one :assignment
has_one :student, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :student
belongs_to :computer
end
Does this seem like the best way to handle this problem? Or something better sound out quickly to the experts here. Thanks!
You need first to decide if a simple one-to many relationship is enough for you.
If yes, it gets a lot easier, because you can get rid of the Assignment-class and table.
Your database-table "computers" then needs a student_id column, with a non-unique index
Your models should look like this:
class Computer < ActiveRecord::Base
belongs_to :student
end
class Student < ActiveRecord::Base
has_many :computers, :dependent => :nullify
end
"dependent nullify" because you don't want to delete a computer when a student is deleted, but instead mark it as free.
Each of your computers can only be assigned to a single student, but you can reassign it to a different student, for example in the next year.
Actually your approach is fine, as one offered by #alexkv. It is more discussion, than question.
Another thing if you want to use mapping table for some other purposes, like storing additional fields - then your approach is the best thing. In has_many :through table for the join model has a primary key and can contain attributes just like any other model.
From api.rubyonrails.org:
Choosing which way to build a many-to-many relationship is not always
simple. If you need to work with the relationship model as its own
entity, use has_many :through. Use has_and_belongs_to_many when
working with legacy schemas or when you never work directly with the
relationship itself.
I can advise you read this, to understand what approach better to choose in your situation:
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
You can also use has_and_belongs_to_many method. In your case it will be:
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers, :join_table => 'assignments',
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student, :join_table => 'assignments',
end
or you can rename assignments table to computers_students and remove join_table
class Student < ActiveRecord::Base
has_many :assignemnts
has_and_belongs_to_many :computers
end
class Computer < ActiveRecord::Base
has_one :assignment
has_and_belongs_to_many :student
end

Resources