Topic: Which relationships to use?

I'm very new to rails.  This is what I'm trying to accomplish:

I have 3 models. User, Workdate, Job

User:
needs to be able to see all its workdates
needs to be able to see all its jobs

Workdate:
needs to be able to see all its users
needs to be able to see all its jobs

Job:
can belong to more than one workdate
can belong to more than one user


I've tried several approaches, but nothing seems to work quite right. 
Any suggestions? Thanks.

Re: Which relationships to use?

Can you provide a little bit more information about the domain you're modelling here?  What real-world concept is the 'Workdate' class trying to represent?  Ie, in plain english, what is a workdate and how does it relate (in real life) to jobs and workers?

And, when you say "needs to be able to see all of its" do you mean "should be associated with many"?

Last edited by Max Williams (2009-06-12 05:32:14)

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Which relationships to use?

The way I have it now, Workdate is just a date.  It should be associated with all jobs that were done on that specific date.  Also, it should be associated with all users who worked on that date.  What I mean by "needs to be able to see", I would like to do something like:

user = User.first
user.workdates
user.jobs

workdate = Workdate.first
workdate.users
workdate.jobs

I suppose job doesn't need to see what dates or what users it belongs to. Maybe job should have a column for workdate_id and user_id and just enter enter another job record with different days and users (currently my code skips adding new job entries if they exist). 

The job itself can have more than one user or workdate if the job (in real life terms) is rescheduled to another date.  I still want to pull up the same job, just assign it to a different user and date.

I currently have this code in my user.rb model to see the workdates of a user:

http://pastie.org/509696

However, now that I have realized a job can potentially have more than one user (before, I simply put the user_id inside the job sql table) this code no longer works. all_jobs = user.jobs.all is broken since user_id is no longer in the job table.

Re: Which relationships to use?

misterhektik wrote:

The way I have it now, Workdate is just a date.  It should be associated with all jobs that were done on that specific date.  Also, it should be associated with all users who worked on that date.  What I mean by "needs to be able to see", I would like to do something like:

user = User.first
user.workdates
user.jobs

workdate = Workdate.first
workdate.users
workdate.jobs

I suppose job doesn't need to see what dates or what users it belongs to. Maybe job should have a column for workdate_id and user_id and just enter enter another job record with different days and users (currently my code skips adding new job entries if they exist). 

The job itself can have more than one user or workdate if the job (in real life terms) is rescheduled to another date.  I still want to pull up the same job, just assign it to a different user and date.

I currently have this code in my user.rb model to see the workdates of a user:

http://pastie.org/509696

However, now that I have realized a job can potentially have more than one user (before, I simply put the user_id inside the job sql table) this code no longer works. all_jobs = user.jobs.all is broken since user_id is no longer in the job table.

I don't really know if i'm understanding you clearly, but would it be something like this:

User
  has_many :workdays
  has_many :jobs, :through => :workdays

Job
  has_many :workdays
  has_many :users, :through => :workdays

#note not 'Workdate' - a workday represents a user working on a job for one day
Workday
  #fields - user_id, job_id, date
  belongs_to :user
  belongs_to :job

I don't know why you would need all of that code, you can just say

@user.workdays

If i'm missing the point here then feel free to explain your domain more.  EG what exactly is a 'job'?  To me a job is a project that lasts for a certain number of days, or is open-ended.  Maybe a job means something different here.

###########################################
#If i've helped you then please recommend me at Working With Rails:
#http://www.workingwithrails.com/person/ … i-williams

Re: Which relationships to use?

Thanks for the quick reply.
I'll try to clarify.  The user is actually a technician.  Each day, the tech gets anywhere from 5-15 jobs.  The actual job is a work order.  The tech goes to the job, does the work, and leaves to his next job.  Like a cable guy going to various houses.  The job model contains information such as the customers name, home address, work order #, etc.

The problem I had before with user.workdates was that each job had a workdate_id.  If the user has 10 jobs, and i used a has_many :workdates :through => jobs, when I would do user.workdates it would give me 10 entries of the same workdate since each job was reporting back its workdate.  The code was to filter out duplicate entries.

I knew the Workdate model to list a date was redundant, just didn't know how to fix it. I'll definetly try your solution. However, it is now time for me to head to work.

Thanks for the help, I'll follow up this evening.

Re: Which relationships to use?

Should workday even be a model? You could just make a has_many :through relationship like Max said and give Jobs a date field. Then to get user.workdays you write a method that does something like user.jobs.collect(&:date).uniq, but this might not do everything that you might want.

# So we get this
@user.jobs      # given through the association
                # Maybe something like this?
@user.workdays  user.jobs.collect(&:date).uniq
-------------
@job.users      # given through the association
@job.workdays   # Would a job ever have more than one work day?
~@job.date
------------    # Now this part doesn't work so well. You could pick a day and then find
               # all the jobs on that day and further more find the set of workers who
               # did those jobs. Not as nice but how bad do you really need this.
X - workday.users 
X - workday.jobs

Can a Job last for more than one day? If a user has 10 jobs a day that sounds like a Job should only be one day. So a day has many jobs but a job belongs to a day. This make me think that you could have the following schema:
User < AR:B
  has_many :assignments
  has_many :jobs, :through => :assignments

Assignment < AR:B
  #fields - user_id, job_id
  belongs_to :user
  belongs_to :job

Job < AR:B
  has_many :assignments
  has_many :users, :through => :assignments
  belongs_to :workday

Workday < AR:B
  has_many :jobs


My only question then is is there a way to setup a has_many :through relationship where the through model is already joined through another model? (e.g.,
User < AR:B
  has_many :assignments
  has_many :jobs, :through => :assignments
  # has_many :workdays, :through => :jobs

Assignment < AR:B
  #fields - user_id, job_id
  belongs_to :user
  belongs_to :job

Job < AR:B
  has_many :assignments
  has_many :users, :through => :assignments
  belongs_to :workday

Workday < AR:B
  has_many :jobs
  # has_many :users, :through => :jobs, :through => assignments

Re: Which relationships to use?

Max Williams wrote:

User
  has_many :workdays
  has_many :jobs, :through => :workdays

Job
  has_many :workdays
  has_many :users, :through => :workdays

#note not 'Workdate' - a workday represents a user working on a job for one day
Workday
  #fields - user_id, job_id, date
  belongs_to :user
  belongs_to :job

JackVandaL wrote:

User < AR:B
  has_many :assignments
  has_many :jobs, :through => :assignments
  # has_many :workdays, :through => :jobs

Assignment < AR:B
  #fields - user_id, job_id
  belongs_to :user
  belongs_to :job

Job < AR:B
  has_many :assignments
  has_many :users, :through => :assignments
  belongs_to :workday

Workday < AR:B
  has_many :jobs
  # has_many :users, :through => :jobs, :through => assignments

Thank you both for your ideas.  I have come to this:

Job
    has_many     :assignments
    has_many    :users, :through => :assignments
User
    has_many     :assignments
    has_many     :jobs, :through => :assignments
Assignment
    belongs_to    :user
    belongs_to    :job

Now, I just create a new assignment, pass it the user_id and job_id and date and save the object. Each user.jobs and each job.users is updated through the assignment. To get the user.workdates, I do user.assignments.collect(&:date).uniq as JackVandaL pointed out.

All seems to be well now.  Now I just need to go back and fix the rest of my app.  Thanks!

Re: Which relationships to use?

Just as an update (or for anyone who stumbles upon this) there is another thread that covered the issue of (what I'm calling) has_many :through :through. Read it here

My summary of this other thread applied to this threads topic is this (that was a lot of thises... thisi?)

User < AR:B
  has_many :assignments
  has_many :jobs, :through => :assignments do
    def workdays # 'assigned_workdays' might be a better name
      proxy_target.collect(&:workday).flatten.uniq
    end
   end
end

Assignment < AR:B
  #fields - user_id, job_id
  belongs_to :user
  belongs_to :job
end

Job < AR:B
  has_many :assignments
  has_many :users, :through => :assignments
  belongs_to :workday
end

Workday < AR:B
  has_many :jobs do
    def users # 'workers' or 'active_workers' might be a better name
      proxy_target.collect(&:users).flatten.uniq
    end
  end
end


DISCLAIMER: I have not tested ANY of this