I am working in a rails app where a Company has_many orders. An order has a name and that is it. The order can have_many television_spots, radio_placements, and newspaper_placements, the data in these tables is not the same so they have to be split up. Now if an order has_many radio_placements it will not have television_spots or newspaper_placements. My question is it bad practice to have a has_many relationship on a model even if it does not exist?, would it be better better to make a Company have_many television_orders and a television_order have_many television_spots? And thus a Company have_many radio_orders and a radio_order have_many radio_placements, and so on. I think that the first option is dryer initially but using the second strategy could result in cleaner more understandable code as the app grows and what we do with the data changes.
It's not bad practice to have a has_many association that does not actually have any models associated. The real question is what object type the radio_placements, television_spots and newspaper_placements should really be associated to. If they should in fact be related to the Order model, then place the associations there. From my understanding of your question/data shape it would appear that you do want these relationships to be on the Order model. So something like:
class Company
has_many :orders
has_many :television_spots, through: :orders
has_many :radio_placements, through: :orders
has_many :newspaper_placements, through: :orders
end
class Order
has_many :television_spots
has_many :radio_placements
has_many :newspaper_placements
end
Hopefully that helps.
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'm sorry for the titlegore but I am not sure how else to describe what I am trying to do. Maybe my explanation will clear it up.
The code:
user.rb
has_many :user_roles
has_many :roles, through: :user_roles
category.rb
has_many :category_roles
has_many :roles, through: :category_roles
What I would like to do now is to create a ActiveRecord relation with which I can select all categories that a given user also posses a relation for. (Basically a user can only access categories he has the given roles for.)
Is there a way to chain this into a where clause? Or is a complete different approach easier? It would have to be as elegant as the one I use right now (except this problem)
Right now I have it solved by getting all categories and filtering them in the ruby side. This is not a big problem as it is only needed at one point and there will only be around 10 categories max so this is something I am willing to sacrifice.
For example:
A User has roles User and Admin.
A Category needs the role Admin and Minecraft
Then the user should not be allowed to view this given Category, if he had the role Minecraft as well then he should be able.
I'm still making my way with Rails and I have a question about associations.
I'm building a fitness website and I want to have users track their workouts. I'm a bit unsure as to how the associations should go. What follows is what I currently have.
A Workout is made up of a group of exercises. The user would create a workout object to save all the exercises together so as to not have to repeat the creation process every time. On top of that I don't want them to have to re-create exercises to add it to a new workout. So, both workouts and exercises would belong to a user.
My planned associations are this.
Workout
belongs_to :user
has_many :exercises, :through => :routines
Exercise
belongs_to :user
has_many :workouts, :through => :routines
Routines
belongs_to :workout
belongs_to :exercise
User
has_many :workouts
has_many :exercises
//the rest of the user associations
I think this is correct, but having both workout and exercise belonging to the user seems somewhat redundant to me. Is this the best setup or is there another way to associate these things? Any help is appreciated.
I see there is no need of having Routines there.And you need to tweak your Associations.
How about like this
Workout
belongs_to :user
belongs_to :exercise
User
has_many :exercises
has_many :workouts :through => exercises
Exercise
has_many :workouts
belongs_to :user
Personally, this seems a bit more logical to me:
class User
has_many :workouts
class Workout
belongs_to :user
has_and_belongs_to_many :exercises
class Exercise
has_and_belongs_to_many :workouts
In my opinion exercises only belong to one or more workouts and it has little to do with an user. Therefore I would omit the association between users and exercises.
Since workouts can share similar exercises there is a many-to-many relationship between them. Usually I go for a has_many, through relationship in these cases, but since you do not mention possible additional attributes for the join model has_and_belongs_to_many with a join table should suffice.
EDIT: The associations are probably a bit more complex the more you think about it. For example, a workout can actually belong to multiple users. I think it would be best to go to the drawing board and draw the associations and the attributes per model.
I'm new to Ruby on Rails and was wonder if this is a good setup, or if there is a better configuration.
Background:
The system will be used to assign tasks to users, track the assignor, and allow multiple people to be assigned the task.
Create a company model, user model, task model, and a user_tasks model.
Company Class
has_many :users
has_many :tasks
User Class
belongs_to :company
has_many :user_tasks
has_many :tasks, through: :user_tasks
Task Class
belongs_to :company
has_many :user_tasks
has_many :users, through: :user_tasks
UserTasks Class
belongs_to :user
belongs_to :task
*Tracks assignor with boolean
I think this is perfect. There is one big advantage of having a has_many (model1), through: (model2) association when compared to has_and_belongs_to_many association in that you can access the join model (UserTasks your case) through the ActiveRecord query interface (UserTask.where() or UserTask.find_by(user_id: 1) and so forth). Querying the join table directly can sometimes shorten your queries. If you use the has_and_belongs_to_many association you will have a database table that you cannot directly access in Rails without resorting to SQL.
You probably don't need the UserTasks class if it is just a habtm table. So just create a migration for that table, but skip adding the model. Then in User, do has_and_belongs_to_many :tasks and in Task do has_and_belongs_to_many :users
The other thing that I see is that company is set for both tasks and users. You might have business rules to why this has to be, but if not, you might just be able to say Company -> has_many :tasks, through: :users
I favor :has_many, :through over has_many_and_belongs_to_many or HABTM. For example, I can be assigned to a Task twice, one as a programmer and another time as a designer. So I need some kind of "JobDescription" column on the UserTask column, and I'd need two rows of the UserTask table to represent me being a programmer on a task, and another to represent me being a designer on a task.
You want to write software that is easier to change, even though HABTM is shorter, you might change the software in the future, like in the case of a person doing two subtasks in the same task. Remember that a row in a database is a piece of data, or "a thing", if that makes sense. At the very least, you get timestamps.
A "Project" has_many "ProjectAdmins" and many "ProjectCollaborators".
I've set this up as has_many through relationship:
Project - name:string
Users - email:string
ProjectAdmins - project:references, user:references
ProjectCollaborators - project:references, user:references
Are my relationships correct in my models? It seems wrong to list has_many :users twice.
Project
has_many :project_admins
has_many :project_collaborators
has_many :users, through: :project_admins
has_many :users, through: :project_collaborators
Users
has_many :project_admins
has_many :project_collaborators
has_many :projects, through: project_admins
has_many :projects, through: project_collaborators
It depends on how you want to use your associations.
If a user can not be a collaborator and an admin for the same project at the same time, you could merge the two models to a single model and determine 'admin' or 'collaborator' with a user_type kind of attribute, or with a specific role.
It seems to me a decision that should be driven by your data.
This won't work because has_many, belongs_to, etc methods simply add a bunch of readers and writers to your model. When you try to create an association with the same name, those methods will be overridden.
I would approach this problem differently.
There are several options.
A simpler solution would be to use only one ProjectUser model that would have a role:string (or :references + a separate Role model, if you'd like to keep things normalised) field to your User model. Add a bunch of convenience methods, like #admin? and #collaborator?. And you're all set.
Or you could go with STI (single table inheritance). Create a base class ProjectUser project:references user:references and inherit ProjectAdmin and ProjectCollaborator from it. Alternatively, since ProjectAdmin is probably always a collaborator, you could inherit it from ProjectCollaborator.
Basically, both solutions will give you the same results. But I'd say, the second one is better if you need to have different methods for different ProjectUser types.