Reputation: 8461
I have an object called Tasklist that has many Tasks. Simple enough. But Tasklist belongs to User. I'm trying to create a new task from the tasklists show page, and I'm having a ton of trouble figuring out how to do that. I'll post code and explain my problem further. Any help would be great! Thank you.
def show
@tasklist = current_user.tasklists.find(params[:id])
@task = @tasklist.tasks.new
end
<h1>page<%= @tasklist.name %></h1>
<%= form_for @task do |form| %>
<%= form.label :description %>
<%= form.text_field :description %>
<%= form.submit %>
<% end %>
def create
current_user.tasklists.tasks.create(task_params)
end
private
def task_params
params.require(:task).permit(:description)
end
Here is the error after I try to create the object from the show page.
## ERROR
NoMethodError in TasksController#create
undefined method `tasks' for #<Tasklist::ActiveRecord_Associations_CollectionProxy:0x007faa4263b520>
def create
current_user.tasklists.tasks.create(task_params)
end
Upvotes: 0
Views: 1230
Reputation: 101811
User tasklists
returns a collection of records and you are trying to call the instance method #tasks
on it.
Just think about it - how should ActiveRecord know which task_list in the collection you re trying to add a task to?
First you need to untangle your associations:
class User < ActiveRecord::Base
has_many :task_lists
has_many :tasks, through: :task_lists
end
class TaskList < ActiveRecord::Base
belongs_to :user
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :task_list
# dont use belongs_to as Rails will not keep the relation up
# to date!
has_one :user, through: :task_list
end
Basically we are setting up an indirect relationship between User
and Task
and ensuring the actual foreign key columns are not duplicated.
What you want here is a nested resource:
resources :task_lists, shallow: true do
resources :tasks
end
This will give you these routes among others (run rake routes
to see them all)
Prefix Verb URI Pattern Controller#Action
task_list_tasks GET /task_lists/:task_list_id/tasks(.:format) tasks#index
POST /task_lists/:task_list_id/tasks(.:format) tasks#create
new_task_list_task GET /task_lists/:task_list_id/tasks/new(.:format) tasks#new
This means we can fetch the task list from params[:task_list_id]
.
class TasksController < ApplicationController
before_action :set_task_list, only: [:new, :create, :index]
# POST /task_lists/:task_list_id/tasks
def create
@task = @task_list.tasks.new(task_list_params)
if @task.save
redirect_to @task_list, success: 'Task created.'
else
render 'tasks/show'
end
end
private
def set_task_list
@task_list = TaskList.includes(:tasks)
.where(user: current_user)
.find(params[:task_list_id])
end
def task_list_params
params.require(:task).permit(:description)
end
end
Since we have has_one :user, through: :task_list
we don't have to worry about the current_user when creating a task. We just add one to the task_list.
Now lets update the form so that it points to the right resource:
<h1>page<%= @tasklist.name %></h1>
<%= form_for[@tasklist, @task] do |form| %>
<%= form.label :description %>
<%= form.text_field :description %>
<%= form.submit %>
<% end %>
Upvotes: 4