has_many :through association woes
I have a has_many :through relationship between a few models that almost works.
My goal is to allow users to be assigned to projects and/or tasks. Everything seems to work except user_id's are not being added to new projects. Tasks and comments are fine.
I tried adding user_id's to projects in the console but they do not display in the browser while logged into the user account.
Here's what I'm working with:
PROJECT_ASSIGNMENT MODEL (project join model)
belongs_to :user
belongs_to :project
TASK_ASSIGNMENT MODEL (task join model)
belongs_to :user
belongs_to :task
USER MODEL
has_many :project_assignments
has_many :projects, through: :project_assignments
has_many :task_assignments
has_many :tasks, through: :task_assignments
has_many :comments
PROJECT MODEL
has_many :project_assignments
has_many :users, through: :project_assignments
has_many :tasks, dependent: :destroy
TASK MODEL
has_many :task_assignments
has_many :users, through: :task_assignments
belongs_to :project
has_many :comments, dependent: :destroy
COMMENT MODEL
belongs_to :task
belongs_to :user
PROJECTS#CREATE
@project = Project.new(project_params)
@project = current_user.projects.new(project_params)
@project.save
redirect_to :back
@project = current_user.projects.new(project_params)
doesn't work and neither does @project.update(user_id: current_user.id)
TASKS#CREATE
@project = Project.find(params[:project_id])
@task = @project.tasks.new(task_params)
@task.user_id = current_user.id
@task.save
redirect_to @project
COMMENTS#CREATE
@project_id = params[:project_id]
@task_id = params[:task_id]
comment = Comment.new(comment_params)
comment.update(task_id: @task_id, user_id: current_user.id)
comment.save!
redirect_to project_task_path(@project_id, @task_id)
What gives?
what does it say in your logs? can you create everything via thr console? not seeing accepts_nested_attributes_for. Also make sure all your attributes are being allowed in strong parameters properly
LOGS
Started POST "/projects" for ::1 at 2017-02-01 15:28:03 -0700
Processing by ProjectsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"YBxwzBvHkXAqibYtTZVZI+5BIecKTyzR3hGglcghyfHLOVdTjGkBvl6EPkY+hRSIGE0HXUI7MWvIOxd7AkeZbw==", "project"=>{"project_title"=>"Poolside"}, "commit"=>"+"}
[1m[36mUser Load (0.0ms)[0m [1m[34mSELECT users
.* FROM users
WHERE users
.id
= 1 ORDER BY users
.id
ASC LIMIT 1[0m
[1m[35m (0.5ms)[0m [1m[35mBEGIN[0m
[1m[35mSQL (2.5ms)[0m [1m[32mINSERT INTO projects
(project_title
, created_at
, updated_at
) VALUES ('Poolside', '2017-02-01 22:28:03', '2017-02-01 22:28:03')[0m
Looks like the correct user_id 1 was selected from the db.
But in the console user_id is nil and nothing shows in the browser.
Strong params only need to cover project title, details & due date.
Not familar with accepts_nested_attributes_for
I was assuming you were using nested forms.
What happens if you try building the association via the rails console?
I can build the associations fine via the console, projects just don't show as associated in the browser.
Anyone know why neither of these work from projects#create?
@project = current_user.projects.new(project_params)
@project.update(user_id: current_user.id)
Your project create action looks weird
@project = Project.new(project_params)
@project = current_user.projects.new(project_params)
@project.save
redirect_to :back
# @project = current_user.projects.new(project_params)
# @project.update(user_id: current_user.id)
Why are you doing @project = Project.new(project_params)
and then immediately overriding it with @project = current_user.projects.new(project_params)
?
You should be able to just do:
@project = Project.new(project_params)
@project.update(user_id: current_user.id)
@project.save!
redirect_to :back
I would strongly suggest you start using byebug if you're not already - place it before each of your save methods and then when you execute your commands, byebug should trigger in your console so you can now check all your variables at that moment in time and can tell if your values are what they should be. This should help you pinpoint where the problems are coming from.
Crazy how you can stare at code for so long and still manage to miss something obvious like this, I hadn't noticed I was calling new twice.
user_id's are being assigned to projects properly now, but the projects still aren't displaying in the browser. Both tasks and comments display fine.
Looks like nothing is posting to either of my join tables.
project and task tables are being populated while project_assignments and task_assignments aren't.
How can I change this?
Yeah, countless times I've had to just shut down and come back to it later. It's crazy how you become blind to some of that stuff after staring at it all day!!
On your models, you can clean them up by removing the extra has_many like below. There's no need for the extra has_many declaration for the project_assignments.
# user.rb
has_many :projects, through: :project_assignments
has_many :tasks, through: :task_assignments
has_many :comments
# project.rb
has_many :users, through: :project_assignments
has_many :tasks, dependent: :destroy
# task.rb
has_many :users, through: :task_assignments
belongs_to :project
has_many :comments, dependent: :destroy
Up to now I really haven't ever had to mess with a through association, so doing some extra reading it looks like there is a "special" way to do it right. So in your projects controller, try this out:
@project = current_user.projects.create!(project_params)
redirect_to :back
Your tasks you should be able to rewrite the same way. Let me know how this works out for you.
I played around with the has_many association and it turned out that I did need to specify both has_many :project_assignments
and has_many :task_assignments
But your @project = current_user.projects.create!(project_params)
suggestion did the trick! Projects now show in the browser and project user_id's insert properly into both the projects and project_assignments tables. So that's awesome and I appreciate the help.
Now the only lingering problem is that user id's & task id's do not insert into the task_assignments join table. I've tried a few different ways of assigning the id's, with no luck. That last two attempts were:
@project = Project.find(params[:project_id])
@task = @project.tasks.new(task_params)
@task.update(user_id: current_user.id)
@task.save!
redirect_to @project
@project = Project.find(params[:project_id])
@task = @project.tasks.new(task_params)
@task.user_id = current_user.id
@task.save
redirect_to @project
I'm apparently missing the magic formula to bring my task join table into play.
What kind of queries were you doing that caused problems when you removed them? I have a feeling your queries need to be fixed instead of adding the extra associations back in. I might be wrong on this though, so I'd like to see what your problems were if you don't mind posting the errors and at least one query that caused the errors.
Tasks you would do similar to projects, but instead of creating it with the Project like you do now, you have to create it with the User since the User has_many :tasks, through: :task_assignments
current_user.tasks.create!(task_params)
@project = Project.find(params[:project_id])
redirect_to @project
You need to ensure that task_params includes the project_id so it's properly associated with the project, creating it with the user object will ensure the user_id is present so you shouldn't have to worry about it.
Your suggestion above worked to set the user_id in my project_assignments join table.
After tinkering with this for longer I think the other problem I'm having is being caused by trying to use two join tables, one for projects and the other for tasks.
I'm going to stick with the one join table, see if I can use it to assign projects to users and then later try to add a task_id parameter to the same table, if needed, to assign tasks.