I'm trying to create a linkedin clone, in which users can have several skills. They can also have descriptions for each skill and choose whether the skill is their primary skill or secondary skill (they can have only one of each).
I can't decide whether to just use a has_many or has_many :through.
If I use has_many
class Skill < ActiveRecord::Base
belongs_to :user
validates :description, presence: true
validates :name, presence: true
end
class User < ActiveRecord::Base
has_many :skills, dependent: :destroy
end
Skills table will also have columns primary and secondary, which are booleans.
If I use has_many :through
class Skill < ActiveRecord::Base
has_many :users, through: :users_skills
has_many :users_skills
end
class UsersSkill < ActiveRecord::Base
belongs_to :user
belongs_to :skill
end
class User < ActiveRecord:Base
has_many :skills, through: :users_skills
has_many :users_skills
end
Here, I will have the primary and secondary boolean columns in the UsersSkill model.
Which do you think would be a better choice?
Based on both your stated requirements:
I mainly want people to be able to type in a skill to search for
people with that skill
They can also have descriptions for each skill and choose whether the
skill is their primary skill or secondary skill (they can have only
one of each).
And on just my understanding of the meaning of the models, i.e. that a "Skill" is something which doesn't necessarily belong to a single user -- I'd go with has_many :through.
Reason for this is that the skills are things that stand on their own (and as you said, you want to be able to search for them). Just because one person lists, say, "Scrubbing Floors" as their primary skill, and another as their secondary skill, doesn't mean you should have two entries with "Scrubbing Floors". Have a single Skill for it, and use UsersSkills to assign it to multiple users, along with any information specific to that user's instance of the skill.
You can put the primary/secondary ranking on the users_skills table. The existence of this model will also save you a lot of effort in the future should you ever need to expand the information stored in these mappings.
The bottom line is that the choice depends on one piece of functionality of your system - whether a users' skills are specific to them, or part of a wider group
The has_many relationship will be good if your user will add their skills themselves
The has_many :through relationship will be good if your user's skills can be selected from a wider pool
--
System
You have to remember that since Rails is object-orientated, the setup of your ActiveRecord associations is primarily based on the association of the objects in your system.
many-to-many associations are primarily based on the sharing of objects between two models; whilst one-to-many associations are the result of a model having lots of dependent data
To make the decision, I would simply look at what you've got. If you want to pre-populate the skills, as per the likes of LinkedIn, go for the has_many :through
Related
My group is making a Project Management System for our course, and it's my job that when a user is logged in, that they see the projects they are part of, and also that the projects listed have a members list of current members of that project.
(Also will need an add/delete member function later)
My question is, since the rest of the group have already set it up so that Projects belong to Users, is it possible to have Users belong to Projects in order to set up this member list and do what I'm talking about?
The relation you are describing is not one-to-one:
when a user is logged in, that they see the projects they are part of
This implies that a user can have several projects. You also specified:
[project has a] list of current members of that project
This implies that a projet can have several users.
In conclusion, you need a many-to-many relation between your User and Project models.
This is a basic many-to-many relationship in Rails:
class User < ActiveRecord::Base
has_many :user_projects
has_many :projects, through: :user_projects
class Project < ActiveRecord::Base
has_many :user_projects
has_many :users, through: :user_projects
class UserProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
validates :user_id, :project_id, presence: true
The UserProject model is a join table. What I have done in my code is an explicit has_and_belongs_to_many, which let you have more control over the join table. (example: add a role column in the UserProject table, containing data like project_creator or simple_member)
The UserProject model could be named Membership to be more explicit. I used both models' name to make UserProject, as we usually do in Rails' naming convention.
I am working a side project where a user can have multiple clients. Those client can be of type Person or Business.
I was leaning toward the idea to using STI but I am not sure whether this is the right way to go since my models will not share the same attributes.
For instance a Business has a legal_form where a Person might have a marital_status.
Is it ok to use STI in this particular case or (2nd question) is there any way to allow rails to use separate tables for each types.
STI is like inheritance in ruby. You can use it if you have parent and children and they share a lot of attributes and data. If Person and Business share a lot you can use it. Otherwise I'd recommend you use Polymorphic 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. For example, you might
have a picture model that belongs to either an employee model or a
product model. Here's how this could be declared:
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
I don't really like STI and I'd recommend you try to use Polymorphic Associations.
A common problem with STI
A common problem with STI is that, over time, more types get added to
that table, and it grows more and more columns, and the records in the
table have less and less in common with one another. Each type of
record uses some subset of the table’s columns, and none uses all of
them, so you end up with a very sparsely populated table. Those types
create costs for one another: when you query for articles, you must
remember to filter out all the other types of values and to only
select columns relevant to articles, or else pay a huge performance
cost. You reproduce a lot of work the database would do for you, if
you only had each data type in its own table.
Assuming I would go for the polymorphic association would this be correct
class Client < ActiveRecord::Base
belongs_to :cliental, polymorphic: true
end
class Business < ActionRecord::Base
# Here I am using has_one because I do not want to have duplicates
has_one :client, as: :cliental
end
class Person < ActionRecord::Base
# Here I am using has_one because I do not want to have duplicates
has_one :client, as: :cliental
end
And later I would like to do the following
class User < ActiveRecord::Base
has_many clients
has_many businesses, through: :client
has_many people, through: :client
end
I'm fairly new to rails and ActiveRecord and I'm trying to find the correct way to model my data.
I am building an application that let's swim instructors put together a class plan that shows what skills they will be teaching their class and what activities they will use to teach each skill. A Plan can contain many Skills and each Skill can have many Activities associated with it.
On the Plan form there is a widget for the skill-activity combination. In it, user should be able to select a Skill from a dropdown and for the selected skill select multiple activities from a list. This widget can repeat any number of times on the form.
My current model:
Class Plan
has_many :plan_activities
end
Class PlanActivities
belongs_to :plan
belongs_to :skill
has_and_belongs_to_many :activities
end
Class Skill
end
Class Activity
end
Is this model correct? My problem with it is that accepts_nested_attribtues_for does not work for HABTM associations. I've read that I can replace it with has_many through:, but that would mean adding yet another join model to the picture. It just seems a little too ugly. Is there a better way to do this?
EDIT:
My Skills and Activities are in list form and I should be able to include the same skill and/or activity on multiple plans.
Your model is very close. One improvement here would be to completely remove the PlanActivities model. Just have the associations directly on the plan. At this point I don't think the extra model is justified. We have a pretty basic pyramid structure here. At the top is plan with many skills, Each skill has many activities, so a simpler model would look like:
class Plan
has_many :plan_skills
has_many :activities, through: :plan_skills
end
class Skill
has_many :activities, through: plan_skills
has_many :plan_skills
end
class PlanSkill
belongs_to :plan
belongs_to :skill
has_many :activities
end
class Activity
belongs_to :plan_skill
end
The join model is an option but not entirely necessary here. Plan.activities will make the needed join table for you.
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.
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