Planning a Rails application associations - ruby-on-rails

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

Related

Ruby on Rails has_many :through in a polymorphic association

I have searched and searched and found only partial solutions to my current question.
The thing is, I'd like to know if it is possible to use has_many :through along with a polymorphic association in Ruby on Rails.
I have a system where students can create travel plans (that can belong to many students) and refund claims (that can belong to only one student) for their projects. In this system, both admin users and students are able to comment on the plans and claims.
My associations are:
class Student < ActiveRecord::Base
has_and_belongs_to_many :travel_plans
has_many :refund_claims
has_many :comments, through: :travel_plans
has_many :comments, through: :refund_claims
end
class AdminUser < ActiveRecord::Base
has_many :comments
end
class TravelPlan < ActiveRecord::Base
has_and_belongs_to_many :students
has_many :comments, as: :commentable
end
class RefundClaim < ActiveRecord::Base
belongs_to :student
has_many :comments, as: :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
My questions are:
Is it correct to associate comments twice in the Student model?
I don't want the AdminUsers to have travel plans and refund claims, how can I identify their comments as being made on a travel plan or on a refund claim?
Would there be a better approach?
Thanks a lot in advance for everyone!
Cheers,
You probably want to add an polymorphic author attribute to the Comment model. Than you just need has_many :comments, as: :author to the Student and AdminUser model.
If this is a new application and you are starting on the green field you might want to rethink your models a bit and add a Role and a User model. Student would than be a role of user as would AdminUser be.
Is it correct to associate comments twice in the Student model?
No, not really. If you have duplicate association name, you can only use one of them. If you want to use both, you have to name them differently.

Double entry table in rails

I would like to create a double entry table form according two models.
For now I'm able to create a simple table with the members of a communities
on the columns, I must add the informations of an other model, like this :
My models :
Community
has_many :memberships
Membership
belongs_to :user
belongs_to :community
User
has_many ::memberships
has_many :skills
Skill
belongs_to :user
belongs_to :community
I there some gem existing to make a double entry table or is it easier to make it from scratch? if so, how can I begin ?
It seems like you would benefit from a through relationship here.
Instead of referencing community directly from the skill table, you could do:
Skill
belongs_to :user
has_many :communities, :through => :user
On user, add:
has_many :communities, :through => :memberships
Wouldn't this get the link between skill and community that you would like?
As Jay mentioned, you would benefit from a has_many :through relationship, or maybe a has_and_belongs_to_many relationship; whether it's the actual solution we'll have to see:
#app/models/user.rb
Class user < ActiveRecord::Base
has_many :memberships
has_many :skill_users
has_many :skills, through: :skill_users
end
#app/models/skill_user.rb
Class SkillUser < ActiveRecord::Base
belongs_to :skill
belongs_to :user
end
#app/models/skill.rb
Class Skill < ActiveRecord::Base
has_many :skill_users
has_many :users, through: :skill_users
end
This will allow you to associate each user (note that members are different than users) with specific skills without using double-entries in your tables
Relational
The basis of what you're seeking can be found in Relational Databases
These work by storing data in single instances, and linking to other data through foreign_keys. These foreign keys are things such as user_id etc:
(more information here)
This means instead of populating the same data twice, it is correct to reference that data from other models, as required. This is where join models come in
Join Model
Join models allow you to "link" two pieces of data through a join model:
For you, it means storing your skills in its own model, and linking users with skills on a join model (I've called skill_user.rb). This means that you'll be able to call your user's skills like this:
#user.skills #-> goes through the join model

Rails Many-to-Many Relantionship Dilemma

I have a User model:
class User < ActiveRecord::Base
has_many :projects, dependent: :destroy
end
and a Project model:
class Project < ActiveRecord::Base
belongs_to :user
end
What should I do if I want a User to be able to fund Projects, and a Project can be funded by many Users?
This would mean I get a Many-to-many relationship, and I would need an additional intermediate table. Call it user_projects:
class UserProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
But how do I cope with the previous relationship I had between the models before I implemented the third one?
How do I know which project belongs to which user if I have the intermediate table?
Would I modify the tables the following way?
class User < ActiveRecord::Base
has_many :projects, through: :user_project, dependent: :destroy
has_many :user_projects
end
class Project < ActiveRecord::Base
has_many :user_projects
has_many :users, through: user_project
end
Whether or not you need an intermediate table depends on if there is any associated data you need to store with the user/project pair -- such as a dollar amount or date info or role, etc.
If you don't need to store anything else, then just use a HABTM relationship. Otherwise, your final solution would be the way to go.
I personally don't like the choice of 'user_project' as it's too close to the HABTM's 'users_projects'. Perhaps something like ProjectMember or ProjectFunder or Funding would be better, but it kind of depends on what extra data you need to store.

Creating a many-to-many relation in Rails

So I'm Rails n00b and I want to create a "favorites" relationship such that a User can have many favorite Item. I'm not entirely sure how to do this, this is how I'm going to try but I'm not sure if this is a good practice at all:
class User < ActiveRecord::Base
has_many :favorites
//other code
end
class Favorite < ActiveRecord::Base
belong_to :user
has_one :item
end
class Item < ActiveRecord::Base
belongs_to :item
end
Is this a good way to do it? Should I be using has_and_belongs_to_many ?
I'm specially concerned in the following scenario: Say a user has 100 favorite items.
When I do a User.find(id) will I also be retrieving the 100 favorites and the 100 Items?
In case it's important: ruby version 1.9.3, rails version 3.2.11
Can you try has_many => :through?
class User < ActiveRecord::Base
has_many :favorites
has_many :items, :through => :favorites
//other code
end
In your case has_many :through is definitely the way to go. I would recommend reading: http://guides.rubyonrails.org/association_basics.html
Of particular interest with regard to your question:
2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly:
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
class Assembly < ActiveRecord::Base
has_many :manifests
has_many :parts, :through => :manifests
end
class Manifest < ActiveRecord::Base
belongs_to :assembly
belongs_to :part
end
class Part < ActiveRecord::Base
has_many :manifests
has_many :assemblies, :through => :manifests
end
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
It is better than using has_and_belongs_to_many.
When I do a User.find(id) will I also be retrieving the 100 favorites
and the 100 Items?
No. You'll just get the user object.
Update:
Calling User.include(:favourites, :items).find(id) will get you joined tables in case you want to make many calls to items table from user object.

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