-1

enter image description hereI'm writing a to-do list app. Each user can have multiple list categories, and each list category can contain multiple list items:

class User < ApplicationRecord
  has_many :list_categories, dependent: :destroy
  has_many :list_items, through: :list_categories

class ListCategory < ApplicationRecord
  belongs_to :user
  has_many :list_items, dependent: :destroy

class ListItem < ApplicationRecord
  belongs_to :list_category

In the index view of the list_category controller I have a list of the categories and a form for creating new categories, which works fine.

Each category in the index view has a link to the show action for that category. In the show view, I have a form for creating new list items for that category (so that the show page effectively acts as the 'new' page for list items) and code for showing the list items already created, but I haven't been able to get either to work.

In the list_categories controller I have:

def show
  @list_category = current_user.list_categories.find(params[:id])
  @list_item = @list_category.list_items.build
  @list_items = @list_category.list_items
end

In the list_items controller:

def create
  @list_category = current_user.list_categories.find(params[:id])
  @list_item = @list_category.list_items.build(list_item_params)
  redirect_to list_category_path(@list_category)
end

private

  def list_item_params
    params.require(:list_item).permit(:content)
  end

The form I'm trying to use is:

<%= form_for(@list_item) do |f| %>
  <%= f.text_field :content %>
  <%= f.submit "Add", class: "btn btn-primary" %>
<% end %>

And the code for showing each list item in the list category:

<% @list_items.each do |item| %>
  <%= item.content %>
<% end %>

When I try to submit a list item in the form in the show action, I get this error in the list_items controller's create action:

Couldn't find ListCategory without an ID

because of the line

@list_category = current_user.list_categories.find(params[:id])

Am I right in thinking including @list_item as the form_for argument automatically maps the form to the list_item create action? Even when I manually put an id number into the error line and try to create a list item, when I call the list_items method on the rrelevant category, it says it doesn't contain any list_items, so the for still doesn't work.

This is the server log:

Started POST "/list_items" for 127.0.0.1 at 2017-08-12 13:33:24 +0100
Processing by ListItemsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"F9kqlwQryyCFsddetAWF4WcI8g2x3c2hMCfaHGWad8QT1OHeHmdvzIFtEoqB8iYyE7vi0CWN+BChFZrpc1VcYw==", "list_item"=>{"content"=>"task"}, "commit"=>"Add"}
User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
Completed 404 Not Found in 3ms (ActiveRecord: 0.1ms)

ActiveRecord::RecordNotFound (Couldn't find ListCategory without an ID):
mef27
  • 379
  • 4
  • 15

2 Answers2

1

Couldn't find ListCategory without an ID

@list_category = current_user.list_categories.find(params[:id])

You are fetching list_category with params[:id] which is wrong considering the params. You should have a hidden_field in the form to store @list_category

<%= form_for(@list_item) do |f| %>
  <%= f.text_field :content %>
  <%= f.hidden_field :list_category_id, value: @list_category.id %>
  <%= f.submit "Add", class: "btn btn-primary" %>
<% end %>

and access it with params[:list_item][:list_category_id] in the list_items#create action

@list_category = current_user.list_categories.find(params[:list_item][:list_category_id]).

Moreover, you are not saving the list_items. You should tweak the create action to below

def create
  @list_category = current_user.list_categories.find(params[:list_item][:list_category_id])
  @list_item = @list_category.list_items.build(list_item_params)
  if @list_item.save
    redirect_to list_category_path(@list_category)
  else
    # your code for failed creation
  end
end
Pavan
  • 33,316
  • 7
  • 50
  • 76
  • Thanks a lot, that code solves it! Had to add 'value:' before '@list_category.id' in the hidden field to avoid the merge error – mef27 Aug 12 '17 at 13:40
  • @mef27 Can I know the reason why you removed the acceptance? – Pavan Aug 13 '17 at 07:44
  • See the more detailed answer below but basically the hidden field violates the strong params, and I don't want to add category_id and user_id to strong params – mef27 Aug 13 '17 at 08:02
0

This worked for a bit, but when I restarted the server etc it started to not let me pass the hidden field, because category_id and user_id aren't in the strong params. However each item needs a category_id to be associated with the right category, and also needs a user_id (i'm planning to display all the user's items, regardless of category, on the home page).

I can't add user_id to the strong params for security reasons and category_id might not be good either because it could result in one user getting access to another user's category. If I remove the hidden fields from the form it won't work either because the model validations will fail. I've been trying to get something with nested routes to work:

resources :users
resources :categories, only: [:create, :destroy, :index, :show] do
  resources :items, only: [:create, :destroy]
end

And in the form:

<%= form_for(@item, url: category_items_path(@category)) do |f| %>

Item model:

class Item < ApplicationRecord
  belongs_to :category
  belongs_to :user
  default_scope -> { order(:deadline) }
  validates :category_id, presence: true
  validates :user_id, presence: true

EDIT ---

Nested routes is the right approach and I got the code to work by following the instructions in the following 2 videos:

https://www.youtube.com/watch?v=HVuawlZTLBw https://www.youtube.com/watch?v=WdaO0-DvjJw

mef27
  • 379
  • 4
  • 15