Rails should relation be polymorphic? - ruby-on-rails

I have 3 tables Injury, Sport, Exercise. I'm trying to wrap my head around if I should create a relation between them. I want to be able to list Injuries in the Sports and Exercises tables; this might expand to more tables in the future, so I'm thinking using a Polymorphic relation. However, that would create a multiple Injuries for both tables, i.e. Injurable_Type would be Exercises or Sports each with there own Injurable_id; if my understanding is correct.
Example:
Injury_id : 1
name = acl tear
injurable_id = 1
type = Sport
Injury_id: 2
name = acl tear
id = 1
type = Exercise
Ideally(though this might be impossible)
Injury_id : 1
name = acl tear
injurable_id = 1
injurable_type = Sport and Exercise
I could keep them separate and store Injury_ids into columns on both Exercises and Sports tables but that will lead to querying issues/headaches down the line I believe. Further more, my understanding of polymorphic associations is to get rid of the Injury_id column.
How should I approach this problem? Leave the tables separate and deal with the possibility of multiple queries down the road or make them polymorphic or something else?
My thought process is this:
Sport / Exercise table would have access to all injuries in Injury table
What I have though of so far:
Injury
belongs_to :injurable, polymorphic: true
Sport
has_many :injuries, as: :injurable
Exercise
has_many :injuries, as: :injurable
Or just leave them separate
Injuries_id: 1, 2 ,3 etc.
Sports table has column
injuries_id: 1, 2
Exercises table has column
injuries_id: 1, 3
Then query them (polymorphic) like:
Sports controller:
sport = Sport.includes(:injuries).find(1)
Exercises controller
exercise = Exercise.includes(injuries).find(1)
Then query them (Separate way) like:
Sports controller:
sport = Sports.find(1)
injuries = sport.pluck(:injuries_id)
injury_name = Injuries.find(injuries).pluck(:name)
Exercises controller:
exercise = Exercises.find(1)
injuries = exercise.pluck(:injuries_id)
injury_name = Injuries.find(injuries).pluck(:name)
Something like that.

My concern with the polymorphic approach is that you won't be able to share the same injury "definition" across different activities. You'll have to duplicate the same injury to attach it to other sports and exercises; this may cause more troubles down the line(and goes against database normalization rules). For example, answering a question such as "how many total ACL tear injuries across all activities?" would not be quickly done(you'll end up matching by text -- that's not ideal!). Also, what if you decided to change the name of the ACL tear injury? You'll end up updating multiple rows in the Injury table.
I would suggest a third approach(similar to your 2nd approach): Injuries should not be directly linked to sports or exercises. The Injury model should do one thDefinehould do it well: Defining what an injury is. This will serve you better as you expand your data schema and add new use cases. Now, associating a sport or exercise activity can be done with a bridge model, i.e.:
class SportInjury
belongs_to :injury
belongs_to :sport
end
class ExerciseInjury
belongs_to :injury
belongs_to :exercise
Then, you're able to associate a Sport model to an Injury model as the following:
class Sport
has_many :injuries, through: :sport_injuries
end
Ruby on Rails should be smart enough to produce optimized SQL when accessing a sport's list of injuries when you call sport.injuries.
This approach has a couple of benefits:
Injuries are not duplicated, and updates to them are done once.
Linking new activities to injuries is straightforward.
You can attach additional data in the bridge entities(e.g., time of injury during a sports match can be stored as an attribute in SportInjury)
Finally, in comparison to your 2nd approach, this is the "RDMS Way." Generally in an RDMS, you encode associations in tables rather than as array columns(which is a practice you may see in a document-based DB such as MongoDB).

Related

Creating a Class from multiple Models - Rails

I have 4 Models, each defined as follows
--Model/Table--
Competition
Matches
Goals
Teams
These 4 models completes a season's worth of soccer league data. The relationship between these tables are already defined. Now, I want to create a class where it picks up data from all 4 models and generate 1, new class. Like so :
class CompetitionSeason < ApplicationRecord
# Use data from all 4 models to construct a soccer season class
end
That is my intention, but I'm not sure if this is the best practice for doing so. It feels... wrong on a Rails framework level. What is the best practice to achieve this?
Thank you.
You should really have another model called Season or some such which owns (has_(one|many)) each of the constituents.
I'm not sure what Competition is.
Match can belong to either a Competition or directly to Season. I don't get what Competition is so I can't make a good recommendation.
Goal should belong to Match or Competition, and not directly to Season. Season can still have many goals through Match or Competition.
Team should have a SeasonTeam junction table.

HMT or HABTM for Orders and Items

Fairly new to rails and trying to understand which relationships to use before going forward.
I have two models: orders and items. This is a many to many relationship, but I'm unsure of which relationship to use.
Orders might have delivery time, quantity of items, etc.
Lastly, what would you call the model joining orders and items if using HMT?
If you need to know anything else about the relationship of the item on a particular order, you need HMT.
If your items change price in the future, do you want to know how much they were sold for on orders in the past?
In this type of requirement, I've always had many "LineItem" records for an order, and the line_item instances belong_to to the item and order, and record the pricing and/or quantity for that order.
HMT vs HABTM? There are so few times that all you need is a many-to-many, that I'd almost always go with HMT for the extra ability to add more information to the association.
This seems like a classic case of HABTM, and the example given in the Rails Guides is perfect. The choice comes down to whether you need any other data or logic on the join model itself. If so, then use the HMT, where you will create a third active_record model to serve as the join table. You can name that anything you want. But it seems like HABTM will work for you, and all you need to setup is the join table with the default name (items_orders) in your migration, and rails will take care of everything else for you.
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end

Look up table, enum or a function to return value from key in Rails

I have two models, Tutor and Student. Tutor can have multiple Topics he can cover, and Student can have multiple Topics he would like to learn. There are 10 possible topics (in string).
I am thinking of creating a Topic table, which contain topic strings. But it would create unnecessary repetition of these strings (making table heavy). So I create a Topic table which contains only topic key.
However, I am undecided about how to retrieve value:
First, I can create another lookup table, which maps a key to string value. This will result in an extra merging step.
Second, I can have a class function that belongs to Topic, that returns string from value.
Which way would be more efficient in my situation? Is there a better approach that I haven't thought of?
Thank you.
It depends. IMO "topics" sounds like something that need managing, and may change.
If that's the case, there should be a topic table, with an id, name, probably a description, etc. Both tutors and students would have_many topics :through a join table. Topics would belong_to both.
There are several implementations options, including a polymorphic association of topics.
Assuming a Tutor model could be rolled into a User model with role assignments, setup a has_and_belongs_to_many relationship between Users and Topics. This sets up a join table where the foreign keys are listed to join the heavier rows together.
class User < ActiveRecord::Base
has_and_belongs_to_many :topics
end
class Topic < ActiveRecord::Base
has_and_belongs_to_many :users
end
See the Rails Guide for additional description.
The alternative is to use just a has_many association but it lacks a join table so the Topic entries will need to be duplicated for each instance.

Rails - Single Table Inheritance or not for Applicant/Employee relationship

I am am developing a recruitment application for storing records of people who come for an interview. So I have two models - Applicant & Employee which seem like one in OO sense. i.e. Employee was earlier an applicant. Therefore I am planning to create a Single table inheritance like:
Applicant < Person
Employee < Person
All the fields of Applicant are there in Employee. Employee has several other fields that are not in Applicant.
Am I correct with this plan. I have another similar scenario to deal with - prospects & clients.
Update: As I am working more and more with STI, I am starting to dislike it.
As per my personal opinion , in OO point of view you are correct.
Applicant < Person
Employee < Person
But when it comes to database/ tables i will go for a one table called 'persons' (Person) that has all the columns for both 'Applicant' and 'Employee' and have a column with a flag indicating whether the Person is an Applicant or Employee
NOTE :: but this is only true if you have ONLY 'several other fields' not many
HTH
sameera

Complex Rails associations question

I have three models of concern here:
User
Fight
FightPunches
Punches
The associations are as follows:
User has many fights, foreign_key => 'challenger_id or challengee_id'
Fight belongs to challenger, as User
Fight belongs to challengee, as User
Fight has many fight_punches
FightPunches belongs to fight
Fight has many punches, through fight_punches
FightPunch belongs to Punch
FightPunch belongs to User
Key notes:
There are three fk's in the FightPunch model: fight_id, punch_id, user_id
challenger_id and challengee_id reflect the two users who are fighting in the Fight model.
Here is the challenge. I want to create two associations in the Fight model:
has_many challenger_punches
has_many challengee_punches
The first must grab the records from the Punch model; however, it must only grab those records where Fight.challenger_id = FightPunch.user_id.
Same with #2, but just dealing with the challengee.
belongs_to documentation look under supported options, specifically "foreign_key" and "primary_key"

Resources