【问题标题】:Rails 4 form_for nested resources issueRails 4 form_for嵌套资源问题
【发布时间】:2016-11-03 02:01:25
【问题描述】:

我研究过类似的问题,但我认为他们没有解决我的特定问题:
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 方法时,我收到以下错误消息:

#UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138 的未定义方法“to_key”>

我的项目视图的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

项目.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

【问题讨论】:

  • 我怀疑你收到了错误,因为@user_story 不是 UserStory 的实例

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


【解决方案1】:

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

1) before_action :set_user_story

这将使用param[:id] 找到正确的@user_story 模型对象并自动使其可用于正确的方法。在这种情况下,它是 excepted 为 :create,但也应该排除在路由中没有 :id 的其他方法。改用这个:

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

这将解决(或防止)一些恼人且持久的 ActiveRecord 故障。

2) index 操作

在这种方法中,变量的名称是不符合 Rails 命名约定的标准。该变量当前是单数,但表示 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 动作通常没有可使用的 singular 模型对象,因为此动作用于提供 list 个模型对象。

3) new 操作

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

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

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

4) edit 操作

由于before_action :set_user_story 处理程序已被edit 操作调用,因此无需从edit 操作的主体中查询@user_story;该行可以删除:

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

这实际上将解决报告的原始问题,因为find 的这种形式将(不幸地在这种情况下)返回多条记录,这意味着您将返回一个集合,而不是一条记录。这是此错误消息的实际原因:

#UserStory::ActiveRecord_Associations_CollectionProxy:0x007f456a759138 的未定义方法“to_key”>

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

5) complete 操作

complete 动作是一个自定义的member 动作,这意味着它依赖于: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

很可能在测试期间尚未调用此方法,因为 edit 操作是失败的上游测试。当您开始测试此方法时,这应该会起作用。

6) 密码学

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

class UserStoriesController < ApplicationController

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

  def index
    @user_stories = @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
    @user_story = UserStory.new
    @user_story.project = @project
    # Set other important default values for display now
  end 

  def destroy
    if @user_story.destroy
      flash[:success] = "User story deleted"
    else
      flash[:error] = "User story could not be deleted"
    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])
  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

【讨论】:

  • @user3705134 如果这回答了您的问题,请单击问题旁边的复选标记使其成为接受的答案。完成此操作后,复选标记颜色将变为绿色,表明它现在已被接受。谢谢! :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-03
  • 1970-01-01
  • 1970-01-01
  • 2011-01-16
  • 2011-05-25
相关资源
最近更新 更多