两层has_many/belongs_to

Two layers of has_many/belongs_to

enter image description here我正在编写一个待办事项列表应用程序。每个用户可以有多个列表分类,每个列表分类可以包含多个列表项:

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

在 list_category 控制器的索引视图中,我有一个类别列表和一个用于创建新类别的表单,效果很好。

索引视图中的每个类别都有一个 link 到该类别的显示操作。在显示视图中,我有一个用于为该类别创建新列表项的表单(以便显示页面有效地充当列表项的 'new' 页面)和用于显示已创建的列表项的代码,但我没有都无法正常工作。

在 list_categories 控制器中我有:

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

在 list_items 控制器中:

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

我尝试使用的表格是:

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

以及显示列表类别中每个列表项的代码:

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

当我尝试在 show 操作中提交表单中的列表项时,我在 list_items 控制器的创建操作中收到此错误:

Couldn't find ListCategory without an ID

因为线路

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

我认为包括@list_item 是否正确,因为form_for 参数会自动将表单映射到list_item 创建操作?即使当我在错误行中手动输入一个 id 号并尝试创建一个列表项时,当我在 rrelevant 类别上调用 list_items 方法时,它说它不包含任何 list_items,所以for 仍然不起作用。

这是服务器日志:

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):

Couldn't find ListCategory without an ID

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

您使用 params[:id] 获取 list_category,考虑到 params,这是错误的。您应该在表单中有一个 hidden_field 来存储 @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 %>

并在 list_items#create 操作中使用 params[:list_item][:list_category_id] 访问它

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

此外,您没有保存 list_items。您应该将 create 操作调整为低于

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

这工作了一点,但是当我重新启动服务器等时,它开始不让我通过隐藏字段,因为 category_id 和 user_id 不在强参数中。但是,每个项目都需要一个 category_id 才能与正确的类别相关联,并且还需要一个 user_id(我打算在主页上显示所有用户的项目,无论类别如何)。

出于安全原因,我无法将 user_id 添加到强参数中,而且 category_id 也可能不好,因为它可能导致一个用户访问另一个用户的类别。如果我从表单中删除隐藏字段,它也不会工作,因为模型验证将失败。我一直在尝试使用嵌套路由来工作:

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

形式为:

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

物品型号:

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

编辑 ---

嵌套路由是正确的方法,我按照以下 2 个视频中的说明获得了代码:

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