Search code examples
mysqlruby-on-railsruby-on-rails-4table-relationships

Rails "has_one" relation. Assign Project to Customer(Timestamp)


I'm building a time tracker. You can create a timestamp with a start time, end time, customer and a project from this customer. So you can see how much time you've spent for a project or customer.

The relationships between tables with "has_many" works perfectly but I have a problem with the "has_one" relation.

My tables:

timestamps              customers             projects
----------              ------------          -----------
id:integer              id:integer            id:integer
desc:string             customer_name:string  project_name:string
customer_id:interger    project_id:integer

My models:

timestamp.rb

class Timestamp < ActiveRecord::Base
    has_one :customer
    has_one :project, through: :customer
end

customer.rb

class Customer < ActiveRecord::Base
   belongs_to :timestamp
   has_many :projects, dependent: :destroy
end

project.rb

class Project < ActiveRecord::Base
    belongs_to :customer  
end

My goals:

  1. Create one timestamp with associated customer and project: Timestamp.create({desc: "Something", customer_id: "1", project_id: "6"})
  2. Get project from timestamp: Timestamp.find(1).customer.project

My problem:

I can make this work if I include a timestamp_id into the projects table but with this method, Rails duplicates every project with the specific timestamp_id when I'm creating a new timestamp. But I want to assign one project_id to the timestamp.

FYI: I'm using rails 4.2.6 with a MYSQL Database.


Solution

  • Because you do not want to have duplicate projects and duplicate customers per timestamp, then you'd need to set only foreign keys to the timestamp. By that you would want to have tables with the following columns:

    Timestamps
      customer_id:integer:index
      project_id:integer:index
    
    Customers
    
    Projects
      customer_id:integer:index
    

    You'll have to write and run migrations to remove columns, and add columns so that it will look above.

    Then, modify associations:

    class Timestamp < ActiveRecord::Base
      belongs_to :customer # change to belongs_to
      has_many :projects, through: :customer # you might not need this anymore because of the line below
      belongs_to :project # add this line
    end
    
    class Customer < ActiveRecord::Base
       has_one :timestamp # change to has_one
       has_many :projects, dependent: :destroy
    end
    
    class Project < ActiveRecord::Base
      belongs_to :customer
      has_one :timestamp # add this line
    end
    

    Then, you could now use the following

    Timestamp.find(1).customer
    Timestamp.find(1).project
    Timestamp.find(1).projects # these projects are customer.projects and are not directly associated to the line above, so I don't think you would need to call this