Rails 4 form_for嵌套资源问题

时间:2016-06-30 19:56:43

标签: ruby-on-rails ruby activerecord erb ruby-on-rails-4.2

我已经研究过类似的问题,但是我不觉得他们已经解决了我的特定问题: Rails form_for results in POST instead of PUT when trying to edit

form_for with nested resources

我是Rails的新手(使用Rails 4.2.5),我正在尝试我的第一个应用程序。我的问题有两个方面:(1)当用户去编辑用户故事时,表单的字段不会填充先前输入的数据(2)重新提交表单时,会创建一个新条目,而不是编辑旧条目数据

我觉得user_stories/edit.html.erb的form_for就是问题所在。当我从表单中取出.build方法时,我收到以下错误消息:

  

未定义的方法`to_key' for #UserStory :: ActiveRecord_Associations_CollectionProxy:0x007f456a759138>

我项目视图的projects/_form.html.erb没有正确的.build方法和功能。但是,我可以使`user_stories / _form.html.erb表单工作的唯一方法是附加构建方法。

这是我的代码:

user_story.rb

class UserStory < ActiveRecord::Base
  belongs_to :project
  belongs_to :user

  include RankedModel
  ranks :row_order
end

project.rb

class Project < ActiveRecord::Base
  has_many :user_stories 
  belongs_to :user
end

的routes.rb

Rails.application.routes.draw do
  devise_for :users
    resources :projects do
      resources :user_stories
    end
  end

  resources :user_stories do
    post :update_row_order, on: :collection
  end

  root 'welcome#index'
end

user_stories / _form.html.erb

<%= form_for([@project, @user_story.build]) do |f| %>
    <div class="form-group">
      <p>As a ...</p>
      <%= f.text_field :param1, placeholder: "type of user", class: "form-control"     %>
    </div>
    <div class="form-group">
      <p>I want ...</p>
      <%= f.text_field :param2, placeholder: "desired functionality", class: "form-control" %>
    </div>
    <div class="form-group">
      <p>so that...</p>
      <%= f.text_field :param3, placeholder: "reason for desired functionality", class: "form-control" %>
    </div>
    <div class="actions">
      <%= f.submit class: "btn btn-primary" %>
    </div>
<% end %>

user_stories_controller.rb

class UserStoriesController < ApplicationController

  before_action :set_project
  before_action :set_user_story, except: [:create]


  def index
    @user_story = @project.user_stories.rank(:row_order).all
  end

  def update_row_order
    @user_story.row_order_position = user_story_params[:row_order_position]
    @user_story.save

    render nothing:true # this is a POST action, updates sent via AJAX, no view   rendered

  end

  def create
    @user_story = @project.user_stories.create(user_story_params)
    redirect_to @project
  end

  def new
  end 

  def destroy
    if @user_story.destroy
      flash[:success] = "User story deleted"
    else
      flash[:error] = "User story could not be deletd"
    end
    redirect_to @project
  end

  def complete
    user_story.update_attribute(completed_at, Time.now)
    redirect_to @project, notice: "User story completed functionality complete"
  end

  def update
    respond_to do |format|
      if @project.user_stories.update(@project, user_story_params)
        format.html { redirect_to project_path(@project), notice: 'User story was successfully updated.' }
        format.json { render :show, status: :ok, location: @user_story }

      else
        format.html { render :edit }
        format.json { render json: @user_story.errors, status: :unprocessable_entity }
      end
    end
  end

  def edit
    @project = Project.find(params[:project_id])
    @user_story = @project.user_stories(params[:id])
  end

  def show
  end

private

  def set_project
    @project = Project.find(params[:project_id])
  end

  def set_user_story
    @user_story = @project.user_stories(params[:id])
  end

  def user_story_params
    params[:user_story].permit(:param1, :param2, :param3, :row_order_position)
  end
end

1 个答案:

答案 0 :(得分:0)

只需要进行一些更改(实际上是调整),我会从上到下进行调整。

1)before_action :set_user_story

这将使用param[:id]查找正确的@user_story模型对象,并自动使其适用于正确的方法。在这种情况下,它except:create,但也应该排除路线中没有:id的其他方法。请改用:

before_action :set_user_story, except: [:index, :new, :create]

这将解决(或阻止)一些烦人且持久的ActiveRecord失败。

2)index动作

在此方法中,Rails命名约定的变量名称是非标准的。该变量当前是 singular ,但表示UserAction模型对象的列表,该对象通常使用plural名称。请改用:

def index
  @user_stories = @project.user_stories.rank(:row_order).all
end

此更改将导致app/views/user_stories/index.html.erb视图中断,其中@user_story变量的任何使用都需要更改为@user_stories。遵守命名惯例有许多直接和长期的好处,因此值得付出额外的努力来改变它以保持一致。

注意index操作通常没有单数模型对象可供使用,因为此操作用于提供< em> list 模型对象。

3)new动作

new操作用于创建和初始化新模型对象以进行编辑。由于不再为before_action :set_user_story操作调用new,因此必须在此处创建@user_story模型对象。此代码将正确执行此操作:

def new
  @user_story = UserStory.new
  @user_story.project = @project
  # Set other important default values for display now
end

此时,您应该能够成功创建一个新的UserStory模型对象,准备好由用户进行编辑。

4)edit动作

由于已为[{1}}操作调用before_action :set_user_story处理程序,因此无需在edit的正文中查询@user_story行动;该行可以删除:

edit

这实际上会修复报告的原始问题,因为def edit @project = Project.find(params[:project_id]) end 的这种形式(不幸的是在这种情况下)将返回多个记录,这意味着您将收回一个集合,而不是单个记录。这是此错误消息的实际原因:

  

未定义的方法`to_key&#39; for #UserStory :: ActiveRecord_Associations_CollectionProxy:0x007f456a759138&gt;

find操作中分配@user_story会覆盖先前从edit处理程序分配的值,并将其替换为不正确的查询结果。

5)before_action动作

complete操作是自定义成员操作,这意味着它取决于complete,就像许多其他操作一样。代码几乎是正确的,除了方法体内使用的:id变量实际上缺少user_story;这最初由@处理程序检索。

before_action

可能在测试期间尚未调用此方法,因为def complete @user_story.update_attribute(completed_at, Time.now) redirect_to @project, notice: "User story completed functionality complete" end 操作是失败的上游测试。当你开始测试这种方法时,这应该有效。

6)Teh codez

更改这些细节将最终确定UserStoriesController,它开始时状态非常好。添加这些更改,这是最终的控制器代码:

edit