All threads / has_many :through association woes

Ask A Question

Notifications

You’re not receiving notifications from this thread.

has_many :through association woes

Adrian DeGus asked in Rails

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.

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 39,609+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.