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.

Lee Jarvis
  • 16,031
  • 4
  • 38
  • 40

2 Answers2

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

Pavling
  • 3,933
  • 1
  • 22
  • 25
  • 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).* – Lee Jarvis Jun 30 '11 at 14:29
  • @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? yes – Lee Jarvis Jun 30 '11 at 14:47
  • 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). – Nuby Jun 30 '11 at 15:06
  • @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) – Pavling Jun 30 '11 at 15:21
  • 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! – Lee Jarvis Jun 30 '11 at 17:40
  • @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.` – BenU Mar 24 '13 at 22:39
  • 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. – Pavling Mar 25 '13 at 07:07
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
Sandip Ransing
  • 7,583
  • 4
  • 37
  • 48
  • 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 ;-) – Pavling Feb 26 '12 at 19:24
  • @Pavling 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. – Sandip Ransing Feb 26 '12 at 19:36
  • 1
    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. – Pavling Feb 26 '12 at 21:33