Which assosciation to use in Rails? - ruby-on-rails

I have a user and project model created in Rails. I have to perform an association that will create an relationship which is described below:
1 User has many Projects
1 project has many Users
How can I go about creating an association for the same in Rails? I need some help on which type of association in rails will help me to achieve this.

You are describing a many-to-many relationship type. Rails allows you to create this relationship using has_many :through and has_and_belongs_to_many directives. You can learn the difference here.
Shortly, has_many :through allows you to add additional columns into the intermediate table, has_and_belongs_to_many doesn't. If you don't need to have additional attributes in the intermediate table than use has_and_belongs_to_many syntax. You can always change to has_many :through later.
class Project < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :projects
end

You are basically trying to have a many-to-many relationship.
In Rails you can do this based on two association concept:
has_and_belongs_to_many (HABTM)
has_many :through
Note:
You should have HABTM if you just do not care about the way these two tables are joined (relationship model) and you do not want to have any logic/validation for your join data. It will just keep your foreign keys in a table and based on that data will be fetched.
You need has_many :through if you want to have an intermediate model in between Project and User which can be called as UserProject model. This way your association could look like as follows:
User Model:
has_many :user_projects
has_many :projects, through: :user_projects
Project Model:
has_many :user_projects
has_many :users, through: :user_projects
UserProject Model:
belongs_to :user
belongs_to :project

You can use has_and_belongs_to_many or has_many through.Here is the link I am providing which will help you to sort out difference between them and which one will be good for you.Here is the video tutorial for you association.ALso there is a good link link.In your case you need has and belongs to many

The best thing to do in this situation is ,
In your user.rb model file:
has_and_belongs_to_many :projects
and In your project.rb model file:
has_and_belongs_to_many :users

You may want too use many to many relationship between project and user. on top of that you may want to visit rails official guide which describes all of these relations in great detail.
http://guides.rubyonrails.org/association_basics.html

Related

rails has_many vs has_many through?

I've read about the relationship identifiers has_many and has_many through. What I can't seem to understand is the difference between them. For example, if I had 3 models, Doctors, Appointments and Patients
class Doctor< ActiveRecord::Base
has_many :appointments
has_many :patients, through: :appointments
end
class Appointment < ActiveRecord::Base
belongs_to :doctor
belongs_to :patient
end
class Patient < ActiveRecord::Base
has_many :appointments
has_many :doctors, through: :appointments
end
Couldn't I just say that Doctor has_many :patients and Patient has_many :doctors and they'd be related? What's the purpose of going through appointments to do this?
thanks
You are right. If you say a doctor has_many :patients and a patient has_many :doctors, then they will be related.
However, I think what this tutorial is getting at is many-to-many association.
If the doctor model and the patient model are related by has_many, then a doctor exclusively owns a patient and a patient owns a doctor. But often, this may not be the case. A doctor can have many patients, and those patients do not have to belong to the doctor exclusively; they might have other doctors.
That's when many-to-many association comes in. In a many-to-many association, an object can have many objects which belong to it but not exclusively. It's just like the association between the doctor model and the patient model.
There are two ways to create a many-to-many association:
has_and_belongs_to_many
has_many #something through: #joining table
In your case, you are using the second way, with the joining table assocation.
Check out this Railscast on detailed explanation of these two. Also, this this official Rails documentation on associations will be helpful.
The only reason to use a 'through' table is when you would like to use some relating data contained in the middle table, in this case the appointment data relating to both doctors and patients.
Also, has_many expects a related belongs_to and visa-versa, so you have to use has_and_belongs_to_many in both models to indicate a many-to-many relationship, and create the appropriate join table to go with it.
Otherwise, yes, you could simply use has_and_belongs_to_many :patients and has_and_belongs_to_many :doctors in their respective files.
Pay particular attention to section 2.8 in the Rails Guide. It may take a few read throughs, but once you get it, it will make sense, I promise.

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

has_many :through seems wrong

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.

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.

Rails (ActiveRecord) many to many table

I have two models, Users and Groups. Each group can have many users and each user can be in many groups.
I currently have something simple like:
User:
has_many :groups
Group:
has_many :users
So I have a groups_users table which is just creating rows with group_id and user_id.
I want to add another column to this, (which I have), the question is how do I access it from a model without using a custom SQL call? In the group model I can go self.users and in user I can go self.groups
Is there a way to change the third column in this table from a user model?
Sorry if this is confusing, please advise on this
Here are a couple of tutorials that should help. Basically there two approaches to make many-to-many work, either has_and_belongs_to_many or has_many :through (recommended).
links:
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
http://railscasts.com/episodes/47-two-many-to-many
http://railscasts.com/episodes/154-polymorphic-association
In Rails 3 you want to make a join table for many to many relationships, using the plural names of the tables you want to join in alphabetical order. So in this case it would be groups_users.
models
class GroupsUser < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :groups_users
has_many :groups, :through => :groups_users
end
class Group < ActiveRecord::Base
has_many :groups_users
has_many :users, :through => :groups_users
end
I [added] another column to [users_groups]...The question is how do
I access it from a model without using
a custom SQL call?
It sounds like you want to access a column of your user_groups table by calling a method on your User model or your Group model.
Some suggestions:
I'd name the table "user_groups" to work with ActiveRecord's pluralization expectations, but I'm not sure if that's essential.
Following Dave's advice, you'd want to set things up using the "has_many :through" technique...
# Declare a Model based on the many-to-many linking table.
class UserGroup < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :user_groups
has_many :groups, :through => :user_groups
end
class Group < ActiveRecord::Base
has_many :user_groups
has_many :users, :through => :user_groups
end
Is there a way to change the third column in this table from a user model?
This is a little unclear, but keep in mind that each User can have a lot of UserGroups. So if you wanted to change that third column you'd have to find the particular one you're looking for.

Resources