2

New to rails (using 4.1), making a small project management tool as a learning project. I have run into a slightly more complex model association, and I want to make sure I am on the right track here, and ask how to extend it a bit.

Consider this situation:

Models:

class Website < ActiveRecord::Base

    has_many :website_user
    has_many :users, through: :website_user
    has_many :tasks, through: :website_user

end

class User < ActiveRecord::Base

    has_many :websites, through: :website_user
    has_many :website_user

end


class WebsiteUser < ActiveRecord::Base

   belongs_to :website
   belongs_to :user
   belongs_to :role
   has_many :tasks

end

class Task < ActiveRecord::Base

    belongs_to :website_user
    has_one :website, through: :website_user

end

class Role < ActiveRecord::Base
    has_many :website_user
end

DB:

create_table "roles", force: true do |t|
    t.string "name"
end

create_table "tasks", force: true do |t|
    t.text    "description"
    t.string  "title"
    t.integer "website_user_id"
end

create_table "users", force: true do |t|
    t.string "name"
    t.string "email"
    t.string "password"
    t.string "password_hash"
    t.string "password_salt"
end

create_table "website_users", force: true do |t|
    t.integer "website_id"
    t.integer "user_id"
    t.integer "role_id"
end

create_table "websites", force: true do |t|
    t.string "name"
    t.string "url"
end

What I have going on here is basically Websites get users (team members working on sites) associated though the website_user table. That table belongs to roles, so that a team member would have a specific job on this website, and finally, tasks belong to the website_user association, so that you could swap out a user, but the task would stay associated with the role and website.

I am looking into extending it more, so that the task would be associated on website_user twice, once for the assigner, once for the assigned user of the task. However, at this point, it feels like I will have an awful lot of things attached to a big join table in the middle, and without a ton of experience under my belt, it is starting to smell like there might be a better way.

If this all looks good, how would you join the tasks to the website_user twice, once for assigner, once for assigned? Or alternatively, how would rearrange the model association?

3 Answers 3

1

A simple solution that first comes to head is to keep assigner and assignee ids in Task model.

Migration AddAssigneeAssignerToTask

class AddAssigneeAssignerToTask < ActiveRecord::Migration
  change do
    add_reference :task, :assignee, index: true
    add_reference :task, :assigner, index: true
  end
end

Adding belongs_to into Task model

class Task < ActiveRecord::Base

  belongs_to :assignee, class: 'WebsiteUser'
  belongs_to :assigner, class: 'WebsiteUser'

  has_one :website, through: :assignee
end

Modifying WebsiteUser

class WebsiteUser < ActiveRecord::Base

  belongs_to :website
  belongs_to :user
  belongs_to :role

  has_many :assigned_tasks, class_name: 'Task', foreign_key: 'assigner_id'
  has_many :received_tasks, class_name: 'Task', foreign_key: 'assignee_id'
end

So afterwards you can use it like this

@website_user.assigned_tasks # => []  
@website_user.received_tasks # => [Task1, Task2]

BUT

If you think to add some different functionality to either assigner or assignee, you should consider to use STI or MTI

Sign up to request clarification or add additional context in comments.

Comments

1
class Task < ActiveRecord::Base
  belongs_to :assignee, class_name: WebsiteUser, foreign_key:website_user_id
  belongs_to :assigner, class_name: WebsiteUser, foreign_key:assigner_user_id
end

class WebsiteUser < ActiveRecord::Base
  has_many :assigned_tasks, class_name: Task, inverse_of: :assignee, dependent: :destroy, foreign_key: :website_user_id
  has_many :tasks_assigned, class_name: Task, inverse_of: assigner, dependent: :destroy, foreign_key: :assigned_user_id
end

You will have to add another foreign key in your tasks table..

just a starting point but this should get you going..

Comments

1

I can not advice you in the database design, but you can assign users twice using an option called class_name. You can read more here: http://guides.rubyonrails.org/association_basics.html#belongs-to-association-reference

But you will have to add additional foreign_key to your Tasks model as well.

And I also advice you to read following chapter of M. Hartle book, as it have really good explanation between relationships of models: https://www.railstutorial.org/book/following_users#cha-following_users

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.