Blease
Blease

Reputation: 1420

Rails association - Customised list of items from existing list

I am developing a rails app where users can add tasks they wish to do to a customised list of their own. Each task can also belong to 0 or more categories. So far I've tried this:

user.rb

has_one :user_list
has_many :tasks, through: :user_list

user_list.rb

belongs_to :user
has_many   :tasks

tasks.rb

has_and_belongs_to_many :categories

[timestamp}_migration.rb

create_table :user_lists do |t|
  t.integer :user_id
  t.integer :task_id

  t.timestamps null: false
end

The issue I am having is in the console I try to run User.find(1).tasks it cannot find the column tasks.user_list_id when using the following query:

SELECT "tasks".* FROM "tasks" INNER JOIN "user_lists" ON "tasks"."user_list_id" = "user_lists"."id" WHERE "user_lists"."user_id" = ?  [["user_id", 1]]

This query should be joining the tasks id from the tasks table with the tasks id on the user_lists table. Are the associations correct and if so what can I do to change the query?

Upvotes: 0

Views: 45

Answers (2)

Alan Lattimore
Alan Lattimore

Reputation: 101

Your use case calls for a task to be assigned to more than one user and a user has only one task list. This sounds like a HABM association between users and tasks.

The simplest way to express that would be:

class User
  has_and_belongs_to_many: :tasks
  ...
end

class Task
  has_and_belongs_to_many: :users
  ...
end

and a migration to create the join table:

create_join_table :users, :tasks, do |t|
  t.index :user_id
  t.index.task_id
end

You don't need to create a TaskUser model to match the join table until you need to track additional attributes. Rails will take care of that automatically.

If a user needs more than one task list, you'll need that TaskList model. Let me know and I'll update my answer.

Here's the documentation on HABM and join migration

Upvotes: 0

max
max

Reputation: 102222

To allow tasks to be placed on many lists you need a M2M join table which joins the user_lists and tasks table.

class User < ActiveRecord::Base
  has_many :user_lists
  has_many :tasks, through: :user_lists
end

class Task < ActiveRecord::Base
  has_many :user_list_items
  has_many :user_lists, through: :user_list_items
end

class UserListItem < ActiveRecord::Base
  belongs_to :task
  belongs_to :user_list
  has_one :user, through: :user_list
  # optional
  validates_uniqueness_of :task_id, scope: :user_list_id
end

class UserList < ActiveRecord::Base
  belongs_to :user
  has_many :user_list_items
  has_many :tasks, through: :user_list_items
end

You can create the join model and migration with:

rails g model UserListItem user_list:belongs_to task:belongs_to

You may also want to open up the migration and add a compound index:

add_index :user_list_items, [:user_list_id, :task_id], unique: true

Setting it as unique is optional - but in most cases you want join table entries to be unique for table A and B.

See:

Upvotes: 3

Related Questions