Making Model Associations to make Queries easier - ruby-on-rails

I've came up with a Database Schema, but I feel it's more influenced by my old SQL habits than a Rails ORM. Here is the schema I developed.
Currently, I have made models for all the tables in the image above, however, after researching associations, I believe that I can just use Rails Associations to produce some of the many-to-many relational tables.
In my PlayerStats#index I find myself doing things like <%= Year.find(TeamYear.find(TeamUser.find(player_stat.team_user_id).team_year_id).year_id).year %> which I know is not good. So, I'm looking for guidance on how to build model associations based on the normalized schema above. Here is my guess:
User.rb
has_many :team_users
Team.rb
belongs_to :team_year
Year.rb
belongs_to :team_year
Team_Year.rb
has_many :teams
has_many :years
Team_User.rb
belongs_to :user
belongs_to :team_year
has_one :role
Game.rb
has_many :team_years
Player_stats.rb
belongs_to :team_user
belongs_to :game
I'm not sure if I'm on the right track or not. Furthermore, I'm not sure how to take advantage of declaring these associations. I'd definitely like to make the Team.name (given Player_Stat.game_id) a lot easier than Team.find(TeamYear.find(Game.find(player_stat.game_id).away_team_year_id).team_id).name.
Am I trying too hard to have a normalized DB? Should I be thinking about this problem differently? I appreciate any help or guidance.

Look on has_many through association.

Your DB Design is fine...
Consider for e.g. the common one to many relationships:
User model, and a Subject model having userid with it..
Now in sql terms you would put a user id in Subject. Subject[ id, userid, marks]
so in subject model you will define userid belongs_to User..
Read http://guides.rubyonrails.org/association_basics.html this in detail atleast once..and maybe try it a little in rails console with one two tables... and you will be good to go...
Also http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html has nice and clear explanation with example look from heading 'Cardinality and associations' in it...

The DB Design seems OK, except the Game-TeamYear relation, witch is unclear, but the direction of the query is not just what is easier to do.
Try starting the building not from the top, but from the base, in your case your query
<%= Year.find(TeamYear.find(TeamUser.find(player_stat.team_user_id).team_year_id).year_id).year %>
transforms into
<%= player_stat.team_user.team_year.year %>
and the query
Team.find(TeamYear.find(Game.find(player_stat.game_id).away_team_year_id).team_id).name
transforms into
TeamYear.find(player_stat.game.away_team_year_id).team.name
So you just go from inside query to the outside query, hope you got the idea.

Related

Object that belongs to multiple other objects

Take the following associations based on a Feedback model:
feedback.rb
belongs_to :buyer, :foreign_key => :buyer_id, :class_name => "User"
belongs_to :seller, :foreign_key => :seller_id, :class_name => "User"
belongs_to :listing
The reverse associations are has_many
When creating the feedback object it could be 'anchored' to any of the above other objects (e.g. buyer.feedbacks, seller.feedbacks, listing.feedbacks)
Say in this example we want to primarily link it with the buyer and so nest the feedback routes within user and then build a create action in the Feedbacks controller that looks something like:
current_user.feedbacks.new(feedback_params)
or is it more appropriate/correct to reference?
buyer.feedbacks.new(feedback_params)
Is this even valid without an explicit buyer model?
What is the Rails way to then incorporate the other belongs_to relationships?
It's not a nested attribute (as the other objects already exist).
Should I be merging in the other params with something like?
params.require(:feedback).permit(:rating, :comment).merge(seller_id: #seller.id, listing_id: #listing.id)
One approach I have seen is the use of a before_validation filter in this sort of manner (from a different domain) like this:
class Feedback < ActiveRecord::Base
belongs_to :user
belongs_to :host, :class_name => "User"
belongs_to :inquiry
belongs_to :room
before_validation :set_room_and_guest_id
def set_room_and_guest_id
self.room ||= self.inquiry.room if self.inquiry
self.user ||= self.inquiry.user if self.inquiry
end
I have spent a long time reading related posts about this today as well as the Rails documentation and have not been able to find a conclusion.
There's no "right" way here - just build the system you want. It sounds like a Buyer has Feedback about a Listing sold by a Seller, so it's completely reasonable to link all of those things together (or at least Amazon hasn't discovered a reason not to yet, and that's a pretty good sign).
The simplest "Rails way" you're probably looking for is to include all the relevant IDs in the feedback form. That does make them accessible to people who might want to fiddle, eg. by setting the seller/listing IDs to their own and leaving fraudulent positive feedback. You can combat that by merging #seller and #listing in the controller like you've mentioned, or with validations that the reviewer has bought the item, can't leave more than one, etc.
It also sounds like there might be room for a Transaction object here, representing the link between Buyer, Seller and Listing. Maybe the Feedback belongs on the Transaction.
Overall, I'd say you've been paralyzed by all the options available to you, and the best thing to do is pick one and run with it. When it's not clear which direction is best, that's often because you don't have all the information you need yet, but once you try something out you'll quickly learn more. Once you realize "Oh, I should have done this this other way", the beauty of code is you can change it.
I assume every listing has exactly one buyer and one seller, and there can be exactly zero or one feedbacks per listing. Then you might be confusing things a bit by linking the feedback directoy to buyer and seller. The normalized way would be to relate the feedback to the listing and nothing else. Then all questions go away.
(Note, you can then still get the buyer (or seller) directly from the feedback by using a :through rails relation.)
Maybe you are looking for Polymorfic Associations.
A slightly more advanced twist on associations is the polymorphic association. With polymorphic associations, a model can belong to more than one other model, on a single association.

Relationship advice/association logic in rails

I'm designing an application to create goals for a classroom - at the moment, the data relationships are modeled like this
I knew that out of inexperience I might run into problems and now I'm beginning to - specifically in my routes file - but also just for generally organizing the site and the data relationships.
When a user signs up, they add a student_group (or class), populate it with students and then add subjects. Later they add goals for each subject - although there should also be goals for a student_group, a student, or even the user. I was thinking of something like this - but would it be better as a has_many, through relationship?
Right now, I've only really done work on the User, Student_group, and Student models and these are fairly straight-forward. A user has many student_groups, and a student_group has many students. I'd like a second opinion before I proceed however, so that I don't have to end up going back and doing things over. Thanks!
I think you might be thinking too far ahead. Once you have your app built around your current data model, you'll know better whether you even want to expand it to include the concept of a goal that isn't part of a student's subject. If you decide that it is, then making goals belong_to a subject, student, or user will be pretty simple. At that point, you could also do something like
Class Student
has_many :personal_goals, class_name: "Goal"
has_many :goals, through: :subjects
def all_goals
goals + personal_goals
end
There's probably a more elegant way to model that last relationship. Would you need to go beyond that? Does it make sense to talk about a student group having a goal of its own? I don't know.
As I gone through your database design, I have found that you should have to use different type of relationships, that rails has provided us. I tried my best to design your schema as per my knowledge. you should define relationship in your model as I suggested below. Any good modification are highly appreciated.
User
has_many :student_groups
has_many :students, through: :student_groups
has_many :goals, as: :goalable
StudentGroup
belongs_to :user
has_many :students
has_many :goals, as: :goalable
Student
belongs_to :student_group
has_many :subjects
has_many :characteristics
has_many :goals, as: :goalable
Characteristic
belongs_to :student
Goal
belongs_to :goalable, polymorphic => true
I have defined some polymorphic associations in your schema. If you need any reference related to these association. visit http://guides.rubyonrails.org/association_basics.html
Hope it will help you. Thanks.

Rails complex association: has_and_belongs_to_many from two classes to one

In my application, I have a User model and Artist model. Both users and artists should be able to "follow" multiple artists. I'm not really sure how to describe this relationship, and I'm completely lost regarding the underlying migration.
I've tried having has_and_belongs_to_many :artists in both classes, and an additional has_and_belongs_to_many :users in Artist, but that seems really messy, and I don't know how to write the migration.
I've looked at the association docs for the migration; I'm not sure if it would be simpler to have a third-class and use a :through association, or if I would need one for user-to-artist and another for artist-to-artist, or if one additional class would be enough.
Help appreciated, thanks in advance!

Should I use `has_and_belongs_to_many` or `has_many :through` in my model?

I have read the Choosing Between has_many :through and has_and_belongs_to_many on the Rails website, however I am a bit confused since I have a different case to the ones given on the website.
I have two models: Prop and CharacterCostume, and the character's costume can have multiple props associated to it, but a prop doesn't belong to that character and it can be used by any number of characters in the scene, too.
Right now I have has_and_belongs_to_many :props inside my CharacterCostume model, which does exactly what I want it to do: it fetches all the props associated with the costume using a table named character_costumes_props when I call CharacterCostume#props
However the association name is putting me off because of the "belongs to many" part. The costume does not belong to any of the props, so there's no has_and_belongs_to_many :character_costumes inside the Prop model.
I know that it can all function fine without it, but it got me thinking that maybe I should use a has_many :through association, but that requires me to create a superfluous model (it is superfluous, right?) and the model would look like this:
class CharacterCostumeProp < ActiveRecord::Base
belongs_to :character_costume
has_one :prop
end
Also, would has_one instead of belongs_to work here?
I want the code to be as semantic as possible, but I am not sure if this will increase the requirement for resources or decrease performance in some way, since there's an intermediate model.
Are there certain quirks/benefits attached to either approach? Is mine good enough? Or is my thinking completely wrong from what I need to do?
Thanks!
I think you want to use a :has_many, :through because you're going to want to work directly with the relation model - what scene(s), consumed or damaged, etc.
But, the reason it reads funny to you is that, for the most part, has_many and belongs_to don't really mean what they mean in English. What they really mean is "They have the foreign keys" and "I have the foreign key", respectively; the exception being the :dependent => :destroy behavior.
That still doesn't really help with has_and_belongs_to_many, since you're then saying, "They have the foreign keys and I have the foreign keys` - except that you can think of it sort of adding a new part both to "I" and "They" that happens to be the same part for each, and has those keys.
Does that help?
The single most important question you must ask yourself when deciding between HABTM and has_many :through is this:
Do I want to store any information specific to the association?
Example 1: magazine subscriptions
A many-to-many relationship between readers and magazines might conceivably be structured as a HABTM or a has_many :through. However, the latter makes far more sense in this case because we can easily think of information specific to the association that we might want to store.
A reader is related to a magazine through a subscription, and every subscription can be described by fields such as price, starting date, issue frequency and whether it's active or not.
Example 2: tags
The relationship between an existing Tag model and, say, an Article model is clearly of the many-to-many kind. The fact that one particular tag has been associated to any particular article must have no influence on whether the same tag will be able to be similarly associated to other articles in the future.
But, differently from the previous example, here the association itself is all the information we need. We just need to know which tags are associated to any given article. It doesn't matter when the association was formed. It doesn't matter how long it lasted.
It may matter to us how many articles a tag is associated with. But that information is stored in the Tag model since it's not specific to an association. There is even a Rails feature that takes care of that called counter_cache.
has_one wouldn't work, you'd need belongs_to
it is not superfluous if you have logic in your association model
has_and_belongs_to_many is good enough for you
See example below
class Student
has_and_belongs_to_many :courses
has_many :teachers, through: :courses
end
class Teacher
has_many :courses
has_many :students, through: :courses
end
class Course
has_and_belongs_to_many :students
belongs_to :teacher
def boring?
teacher.name == 'Boris Boring'
end
end
In the example above, I make use of both versions. See how Course would have its own logic? See how a class for CourseStudent might not? That's what it all comes down to. Well, to me it is. I use has_many through for as long as I can't give a proper name to my association model and/or the model doesn't need extra logic or behavior.

Correct implementation of many-to-many in Ruby on Rails?

Newbie question, beware! I'd like to implement a basic many-to-many relationship in Rails and I'm trying to find out which approach is considered the most "rails way" of them. In a traditional non-ActiveRecord DB I'd just created two tables and a junction table and written a bunch of logic to make sure all three tables are taken in consideration when operations are performed on any of them.
This is my first time using an ORM and so I'm trying to find out if perhaps ActiveRecord somehow simplifies the process for you, perhaps by not requiring a junction table to be manually created.
Railscasts seems like a reputable source of Rails wisdom, are the two ways in this cast truly "Rails way" or can I do better? - http://railscasts.com/episodes/47-two-many-to-many
There's basically two ways: has_and_belongs_to_many (habtm) and has_many with a :through option that points to another association. Both require join tables; the latter is what we call a join model, because you typically add more information to the join.
For example, consider an application with a User model who bookmarks Sites. One way would be to implement it as a habtm relationship
class User < ActiveRecord::Base
has_and_belongs_to_many :sites
end
class Site < ActiveRecord::Base
has_and_belongs_to_many :users
end
user.sites << Site.find(...)
This modeling will also require creating the sites_users table, which necessarily will lack a primary key.
The problem with this is you're likely to want to store additional information on it, so you might as well go with a join model, in this case Bookmark:
class User < ActiveRecord::Base
has_many :bookmarks
has_many :sites, :through => :bookmarks
end
class Site < ActiveRecord::Base
has_many :bookmarks
has_many :users, :through => :bookmarks
#edit: adding validation for requiring at least one bookmark
validate_before_create :at_least_one_bookmark
private
def at_least_one_bookmark
errors.add_to_base("requires at least one bookmark") unless bookmarks.count > 0
end
end
class Bookmark < ActiveRecord::Base
belongs_to :user
belongs_to :site
end
user.bookmarks.create(:site => Site.find(...) )
The more common pattern is the join model approach for its versatility and better modelling, though habtms are still used somewhat. They're just so two-dimensional that you really need to examine what you're doing and make sure there isn't some richer behavior that needs to be modelled as well.
Railscasts is the most reliable source of wisdom :) Rayan Bates teach a true "rails way" to solve problems in ruby on rails applications. You should definitely do it this way.
But if you want to learn more about associations then please follow this link http://guides.rubyonrails.org/association_basics.html
Happy coding!
As others have mentioned, Rails makes this all pretty easy using either has_and_belongs_to_many or has_many though.
I wouldn't say that one is "more right" than the other, it just depends on what you need to do, specifically around how you need to manipulate the joined models. Deciding between them is discussed here.
(All those references are from the association doc already provided.)

Resources