Rails has many and belongs to one
Asked Answered
C

2

12

I have a User model which has many projects and a Project model which can have many users, but also belongs to a single user (ie the user who created this project). It must belong to a User. It also allows a list of users to be associated with it, think collaboration.

With this in mind, my models look like this:

class User < ActiveRecord::Base
  has_many :assigned_projects
  has_many :projects, :through => :assigned_projects
end

class Project < ActiveRecord::Base
  belongs_to :user
  has_many :assigned_projects
  has_many :users, :through => :assigned_projects
end

class AssignedProject < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
end

Now, when I want to create a new project through a User, this is how I would do it:

user = User.create(:name => 'injekt')
user.projects.create(:name => 'project one')

Now, I know that projects is provided through an AssignedProject join model, which is why project.user will return nil. What I'm struggling to get my head around is the best way to assign the project creator (which by the way doesn't need to be user, it could be creator or something else descriptive, as long as it is of type User).

The idea then is to create a method to return projects_created from a User which will select only projects created by this user. Where user.projects will of course return ALL projects a user is associated with.

Assuming this kind of association is fairly common, what's the best way to achieve what I want? Any direction is greatly appreciated.

Capeskin answered 30/6, 2011 at 14:5 Comment(0)
E
20

Add a creator_id column to your projects table for the creator relationship, and then add the associations to the models:

class User < ActiveRecord::Base
  has_many :assigned_projects
  has_many :projects, :through => :assigned_projects

  has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end

class Project < ActiveRecord::Base
  belongs_to :user
  has_many :assigned_projects
  has_many :users, :through => :assigned_projects

  belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many

Elocution answered 30/6, 2011 at 14:27 Comment(7)
Thanks for your answer, this wasn't my issue though. It was: What I'm struggling to get my head around is the best way to assign the project creator (which by the way doesn't need to be user, it could be creator or something else descriptive, as long as it is of type User).Capeskin
@Nuby sure about what? Sure that I'm struggling to get my head around assigning the user/creator to the project in an automated fashion? yesCapeskin
Unless I misread @Pavling's answer (and your question), what he's proposing is that you create a second relationship with the User table, called 'Creator'. You could then call Project.creator to get the associated user from the CreatedProjects association. That would solve (I think) your problem. As far as how to assign it, you could do a before_create hook in the model to assign this to the currently logged in user, or some other user (depending on your project's logic).Self
@injekt - yes, sorry, this code sets up the relationships - how you assign them is up to your code's operation. Personally, I'd have a line in the ProjectsController create action that sets @project.creator = current_user (or whatever helper method you have for getting the logged-in user)Elocution
Well, that's embarrassing. Setting the creator is fine if you think just doing that in the create action of my controller is ok, I think I was looking for unnecessary magic Rails usually provides. One thing I did have wrong though was I had has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id but in project, I had belongs_to :creator, :class_name => 'User' without the foreign key, I guess I assumed it would work this out from the :creator reference. Thanks a lot guys!Capeskin
@Pavling, isn't Project's belongs_to :user redundant after the belongs_to :creator, :class_name => "User", :foreign_key => :creator_id is added? In my case, I'm thinking of changing Project's user_id column name to creator_id.Irritation
Probably/Possibly; only you know whether it's redundant in your code - and if it is, remove it :-) I just added the new associations to show what would be needed. If any of the old code is not needed any more (like the separate :users association on Project), then there's no sense keeping it.Elocution
I
1

I wanted to add little improvement to design. We don't actually need intermediate model because it does not contain any extra column other than reference_ids hence HABTM association is best suited over here.

class User < ActiveRecord::Base
  has_and_belongs_to_many :projects, :join_table => :assigned_projects
  has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end

class Project < ActiveRecord::Base
  has_and_belongs_to_many :users, :join_table => :assigned_projects
  belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
Instillation answered 26/2, 2012 at 18:59 Comment(3)
Really? You don't care what role a user is assigned on a given project, or what their assignment date is, or any other info relevant to that user's participation on that specific project? You'll be refactoring that HABTM out to a HMT before the end of the month ;-)Elocution
@Elocution what user role are you talking about? i am not getting you on this. Is there any need of date on which project assignment got done? If extra columns going to be there then has_many through could be the good choice.Instillation
On one project the user might assume the role of Technical Lead, on another she may be a UI Tester. You may not want this specific information (or assignment date either) - it's just an example; but you will want more information than just who is on a project. Sure, HABTM type relationships do have their place, but really, this one is gonna be a HMT.Elocution

© 2022 - 2024 — McMap. All rights reserved.