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

时间:2016-10-15 21:58:20

标签: ruby-on-rails ruby associations

我有一个名为Tasklist的对象,它有很多任务。很简单。但Tasklist属于User。我试图从任务列表显示页面创建一个新任务,并且我在如何做到这一点上遇到了很多麻烦。我会发布代码并进一步解释我的问题。任何帮助都会很棒!谢谢。

任务列表控制器

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

1 个答案:

答案 0 :(得分:4)

用户tasklists返回一组记录,您尝试在其上调用实例方法#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 %>