How to handle Rails joins table relationship removals?

75 Views Asked by At

I am creating an application which manages Users and Tasks. The relationship between these two models is many-to-many, as a user can have many tasks assigned to them, and a task can have many users assigned to it. For this reason, I created a UserTasks migration and model to serve as a joins table. Looking into the future, I realized I want my frontend functionality to give a user the ability to assign and remove users from a given task. Since I am using a JS frontend, and sending AJAX requests to my rails server controllers, I am not clear on how to handle this functionality. I do not want to remove the user nor the task from the database, but simply the relationship. Is it ok to make an API controller for my UserTask model, and handle this logic in a #destroy method? Is there a more automated approach that Rails provides?

Here's what my models look like:

User

class User < ApplicationRecord
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i

  after_initialize :ensure_session_token

  validate :valid_email

  validates :name, :email, :password_digest, :session_token, presence: true
  validates :email, :session_token, uniqueness: true
  validates :password, length: { minimum: 6, allow_nil: true }

  has_many :user_tasks,
  foreign_key: :user_id,
  class_name: "UserTask"

  has_many :tasks,
  through: :user_tasks,
  source: :task

  ...misc code
end

Task

class Task < ApplicationRecord
  validates :name, presence: true

  has_many :user_tasks,
  foreign_key: :task_id,
  class_name: "UserTask"

  has_many :assignees,
  through: :user_tasks,
  source: :user

  has_many :sub_tasks,
  foreign_key: :parent_task_id,
  class_name: "Task"
end

UserTask

class UserTask < ApplicationRecord
  validates :user_id, :task_id, presence: true

  belongs_to :user,
  foreign_key: :user_id,
  class_name: "User"

  belongs_to :task,
  foreign_key: :task_id,
  class_name: "Task"
end

Current Routes

Rails.application.routes.draw do
  root to: 'static_pages#root'

  namespace :api, defaults: { format: 'json' } do
    resources :users, only: [:create, :update, :show]
    resources :tasks, only: [:create, :index, :show, :update, :destroy]
    resources :projects, only: [:create, :index, :show, :update, :destroy]
    resource :session, only: [:create, :destroy]
  end
end
1

There are 1 best solutions below

0
On

Rails provides ability to manage :has_many records through parent record.

For example you can allow User to accepts_nested_attributes_for :user_tasks with allow_destroy: true option.

class User < ActiveRecord::Base
  accepts_nested_attributes_for : user_tasks, allow_destroy: true
end

After such config you can add/destroy nested attributes through User record update action.

params = {
  id: 'user_id', user_tasks_attributes: [
    { user_id: 'user_id', task_id: 'task_id' }, #this will create new join record
    { id: 'user_task_id', _destroy: '1' } #this will destroy join record
  ]
}

But I'd create a separate controller for UserTasks

Read docs for more information.