Sebastian Limbach Sebastian Limbach - 7 months ago 12
SQL Question

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.

Answer

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
Comments