0

I have a projects app that allows all Users on a team to create Tasks, I'd like a User to be able to add users to a Task (for example if multiple people were in a meeting). Essentially the new task form would have all the usernames of those on the team and the user could check a box by each name for anyone else who participated in the task.

Initially, I thought the best way to do this might be a has_many through: relationship where I had:

Tasks:

class Task < ApplicationRecord
    has_many :user_tasks
    has_many :users, through: :user_tasks

Users:

class User < ApplicationRecord

 has_many :user_tasks
 has_many :tasks, through: :user_tasks

And User_Tasks:

class UserTask < ApplicationRecord
  belongs_to :user
  belongs_to :task
end

The idea was that when a new task is created any user_ids tagged in the task would create new entries in User_Tasks. But I am not 100% sure how to do that in the Task create action and I'm starting to wonder if this is over complicating things?

Is there a simpler way to do this? If not what is the best way to structure a create action around this has_many through: association?

2 Answers 2

1

Its actually much simpler than you think. ActiveRecord creates an _ids setter and getter for has_many associations. The setter will automatically add/delete records in the join table. For example:

@task = Task.create!(user_ids: [1,2,3])
@task.user_ids # => [1,2,3]

Would create a task and three rows in the join table.

You can use this in forms with the collection helpers:

<%= form_for(@task) do |f| %>
  ...
  <%= f.collection_checkboxes(:user_ids, User.all, :id, :name) %>
  ...
<% end %>

Here I am just guessing that your users have a name attribute (used for the text of the checkbox). You can use whatever collection you want instead of User.all.

To whitelist the user_ids param (which is an array) use a hash option with an array as the value:

def task_params
  params.require(:task).permit(:foo, :bar, user_ids: [])
end

This permits an array of permitted scalar types.

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

2 Comments

So do I need to create a :user_ids column in the task? Whenever I do a collection_check_boxes with :user_ids I get an error asking if I mean :user_id setting user_ids: as a param works but it doesn't seem to be recorded anywhere.
This answer and the above really helped me, I'm marking this as correct because I think it provides a bit more context.
1

I think you're pretty much spot on with your associations. UserTask table is being automatically updated by Rails every time you add an object to the collection (either collection of tasks assigned to user or users assigned to a task). So with your current setup you should be able to do:

user = User.first 
task = Task.first
user.tasks << task

which would also automatically create a record in UserTask table.

That way you should be able to pull off in Task create action something like this:

task.users << User.find([1,2])

4 Comments

Thanks for the advice I think I have everything set up but I'm having trouble setting up my Task controller's create action: @task = current_user.tasks.build(task_params) ` @user_task = @task.UserTask.build`
You don't have to do the @user_task part at all - it should be automatically created if you run the first line (tasks.build) and then @task.save. Do you save it?
I'm playing around in the console because I can't get this to work. If I do something like: Task.new(comments: "hello world", users: [1, 2]) I get an error ActiveRecord::AssociationTypeMismatch: User(#70229507233400) expected, got Integer(#70229476918020) if I do t1 = Task.new(comments: "hello world", users: [User.first, User.second]) it will begin to save it but the transaction will rollback.
The message you got is an indication - User object is expected, but instead it gets only integer [1,2]. It should work with users: User.find([1,2]). If this rollbacks it may be because of some validations - read the error message you get and let me know if still in doubt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.