Rails newbie here.
While porting models from Django's to Rails I'v got confused with finding best relationships...
Models i want to implement:
Role (has many Users and Teams)
User (has many Roles and Software)
Software (has many Users and one Team)
Team (has many Software and Documents)
Patch (has many Teams and Software)
Document (has many Teams and Software)
Is it possible to build associations with has_many: through or better use has_and_belongs_to ?
I think :through associations will be complicated, 'cause there is more than two cross-relations with given models.
I can see two ways of doing relationships: one big join model or small join models for all current many-to-many relations between models.
class Role
has_many :relations
has_many :users, through: :relations
has_many :teams, through: :relations
end
class User
has_many :relations
has_many :roles, through: :relations
has_many :software, through: :relations
end
class Team
has_many :relations
has_many :software, through: :relations
end
class Software
has_many :relations
has_many :users, through: :relations
has_one :team
end
class Relation
belongs_to :user
belongs_to :role
belongs_to :teams
belongs_to :software
end
has_many :through gives you the extra flexibility of having a fully functional join model class. This is required in cases where you want to model some aspects of a relationship beyond the simple fact that the two things are associated with one another.
There's no downside to has_many :through as far as i know, other than your codebase might be a line or two bigger, and plenty of advantages. I would always use has_many: through
re the associations being complicated: draw them all out with a paper and pencil (or a UML-drawing app if you prefer), before doing any coding. If you previously had a working django app it should be simple enough to map the schema across: they are both object-relational systems.
Related
I am making a school application where a list of courses is needed for both the lecturers and the students, a list of lecturers for the students and a list of students for the lecturers.
So I have three models: staff, student and course.
I am new to RoR and associations don't make the most sense to me right now, but I want each staff and student to have multiple courses. So I thought has_many and belongs_to. Then I want each student to have a list of his/her lecturers, so has_many :staff, through: :course and the same for staff has_many :student, through: :course. I also want to use a paticular column for the relation, say course_code not an id.
staff.rb
has_many :courses
has_many :students, through: :courses
student.rb
has_many :courses
has_many :staff, through: :courses
course.rb
belongs_to :staff
belongs_to :students
Would this be the best way to do it if I want to be able to do #student.course.all and #student.lecturer.all?
If you want both students and lecturers to have courses, you'll need to use a polymorphic association.
https://guides.rubyonrails.org/association_basics.html#polymorphic-associations
Here's a guide that tells you how to implement!
EDIT: You don't need to use a polymorphic association, but it'll help DRY out your code. In addition, use concerns!
EDIT: Also, I think you need a join table. (A course can have many students, and a student can have many courses).
So a student can follow many courses, staff can teach many courses, a courses is followed by 1 or more students, and it is teached by 1 or more staff. Reading this: you need a join table.
Still as usual, there are many different approaches to do this. Let me start with the simplest first: create two join-tables: followed_courses and lectured_courses.
Class FollowedCourse
belongs_to :student
belongs_to :course
Class LecturedCourse
belongs_to :lecturer
belongs_to :course
Class Course
has_many :followed_courses
has_many :students, through: :followed_courses
has_many :lectured_courses
has_many :lecturers, through: :lectured_courses
Class Lecturer
has_many :lectured_courses
has_many :courses, through: :lectured_courses
Class Student
has_many :followed_courses
has_many :courses, through: :followed_courses
("Staff" is a confusing word in ruby on rails, since it has no plural: how do you indicate a group op staff versus a single staff member, that's why I propose lecturer here, but of course you could use teacher or anything similar).
What I could imagine is that a person, sometimes has the role of student, and sometimes the role of a teacher, so you could have a different join-table, called ParticipatingCourses where it belongs_to a person and a course, and has a role to indicate in which way they participate: student or lecturer. This makes things a little harder, so I think for now it would be simpler to stay with my first proposal.
I am trying to set up the model structure that has a User model Project model along with two join tables setup as has_many through to manage two specific aspects of the Project, ProjectManagers and ProjectMembers.
I can setup two has_and_belongs_to_many but it doesn't feel very railsy.
Right now, this is what I have and I'm unsure of how to proceed to use multiple has_many through (Project Manager, Project Member) both referencing User model.
Would a nested through be the way to go even if a Project Manager will not always be part of the Project User table?
project.rb
class Project < ApplicationRecord
has_many :project_members
has_many :users, through: :project_manager
end
user.rb
class User < ApplicationRecord
has_many :project_managers
has_many :users, through: :project_managers
end
project_manager.rb
class ProjectManager < ApplicationRecord
belongs_to :project
belongs_to :user
end
project_member.rb
class ProjectMember < ApplicationRecord
belongs_to :project
belongs_to :user
end
I don't see any problems with what you're doing. There are other options, but this approach should work as you want. Have you tried it? I'd do something like this.
class Project < ApplicationRecord
has_many :project_members
has_many :project_managers
has_many :members, through: :project_members, :class_name => User.to_s
has_many :managers, through: :project_manager, :class_name => User.to_s
end
Another approach, since the join tables are similar is to subclass them and add a type column to the join table. Not necessarily better than what you're doing.
You could also create a project_users table (don't separate members and managers) and include a "role" column. A scope on project_user.rb would bring back managers or members.
Personally, I would go with your approach. Managers will likely have different auth and have relationships with other objects. It's simpler to query and less likely to make a mistake.
And, I wouldn't recommend a has_and_belongs_to_many, you're likely to add other columns to the join table and you'll be glad you have the model.
My database stores data about a TV show. I want to store information about who worked on what episodes, and in what role.
Each crew member works on many episodes; each episode has many crew members; and crew members can work on the same episode in different roles (they can be writer and director on the same episode, for example).
I'm new to thinking about ActiveRecord, so I'm a bit confused about how to properly represent these relationships in Rails. I would really love it if someone could tip me off as to the 'Rails way' to do this.
My instinct is to create a triple-join (or double-join; three fields, anyway) table. I have three models: CrewMember (has a name), Episode (has a title, airdate, etc), and Role (has a job title, like Director, Writer or Actor). I could create a table with three fields: CrewMember_ID, Episode_ID, and Role_ID. Each row would then describe someone working on a specific episode in a specific capacity.
So the relationship would appear to be:
CrewMember has_many :episodes
Episode has_many :crew_members
CrewMember has_many :roles
Role has_many :crew_members.
Here's where I get confused. Do I create a new model for that table? And if I do, how exactly do I create instances of that object? I understand relationships like Author has_many :books where I can create a book with Author.books.new, but this kind of has-many has-many has-many thing stumps me a bit.
I can do what I want with SQL, I'm just having trouble approaching it from an ORM/ActiveRecord angle.
you can work around this issue by adding a model that collects the three models
rails g model big_model model1:references model2:references model3:references
Using "Jobs" as GreenTriangle suggested
class CrewMember < ActiveRecord::Base
has_many :jobs, dependent: :destroy, inverse_of: :crew_member
has_many :episodes, through: :jobs
has_many :roles, through: :jobs
end
class Episode < ActiveRecord::Base
has_many :jobs, dependent: :destroy, invserse_of: :episode
has_many :crew_members, through: :jobs
has_many :roles, through: :jobs
end
class Role < ActiveRecord::Base
has_many :jobs, dependent: :destroy, inverse_of: :role
end
class Job < ActiveRecord::Base
belongs_to :episode
belongs_to :role
belongs_to :crew_member
validates_presence_of :episode, :role, :crew_member
end
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
this seems pretty basic stuff here, but actually i'm finding it a bit harsh to define this scenario with Rails...
Perhaps any of you can provide some guidance?
So I have three Tables, Users, Invoices, and User_Invoice_Viewers (these basically map users that have viewer access to an invoice)
Now my models :
User.rb :
has_many :invoices
has_many :user_invoice_viewers
has_many :invoices, through :user_invoice_viewers
Invoice.rb
belongs_to user_invoice_viewers
belongs_to :user
User_Invoice_Viewers.rb
belongs_to :users
belongs_to :invoices
Now this just seems wrong... I repeat has_many :invoices on User model, so i expect conflict when executing : User.invoices ...
What would be the best solution for this? I had thought of putting it all on a user_invoice table, but since i expect to have more owners than viewers, for performance reasons, i decided to build a direct dependency between invoice and its owner...
Thanks
I would consider using the :class_name option on the association, so that the two relationships are named differently. Something like this:
class User < ActiveRecord::Base
has_many :invoices
has_many :user_invoice_viewers
has_many :viewable_invoices, through :user_invoice_viewers, :class_name => "Invoice"
...
end