创建属于另一个的对象 - 了解 Rails 关联

Creating an object that belong to another - Understand Rails Associations

我有一个名为 Tasklist 的对象,其中包含许多任务。很简单。但是任务列表属于用户。我正在尝试从任务列表显示页面创建一个新任务,但我在弄清楚如何做到这一点时遇到了很多麻烦。我将 post 编写代码并进一步解释我的问题。任何帮助都会很棒!谢谢。

任务列表控制器

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

这是我尝试从显示页面创建对象后的错误。

## ERROR
NoMethodError in TasksController#create
undefined method `tasks' for #<Tasklist::ActiveRecord_Associations_CollectionProxy:0x007faa4263b520>

def create
  current_user.tasklists.tasks.create(task_params)
end

用户 tasklists returns 记录集合,您正试图对其调用实例方法 #tasks

想一想 - ActiveRecord 应该如何知道您要向集合中的哪个 task_list 添加任务?

首先你需要理清你的联想:

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

基本上,我们在 UserTask 之间建立间接关系,并确保实际的外键列不重复。

你要的是nested resource:

resources :task_lists, shallow: true do
  resources :tasks
end

这将为您提供这些路线(运行 rake routes 以查看所有路线)

            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

这意味着我们可以从 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

因为我们有 has_one :user, through: :task_list,所以我们在创建任务时不必担心 current_user。我们只需将一个添加到 task_list.

现在让我们更新表单,使其指向正确的资源:

<h1>page<%= @tasklist.name %></h1>

<%= form_for[@tasklist, @task] do |form| %>

  <%= form.label :description %>
  <%= form.text_field :description %>

  <%= form.submit %>

<% end %>