【问题标题】:Rails 4.2 Creating Multiple New Records with One FormRails 4.2 使用一个表单创建多个新记录
【发布时间】:2015-12-09 07:42:12
【问题描述】:

我有三个模型:课程、问题和答案。

我要做的是在显示课程视图上显示问题并允许用户为每个答案创建答案。但是,我不确定最好的方法。

我在lesson#showview 上尝试了这种方法:

<% @questions.each do |question| %>
  <%= question.content %><br /><br />
  <%= simple_form_for :answers do |f| %>
    <%= f.input :content %>
    <%= f.hidden_field :question_id, :value => question.id %>
    <%= f.button :submit %>
  <% end %>
<% end %>

使用此代码,我收到错误param is missing or the value is empty: lesson

Answer 有两个字段:content、question_id。

我的另一个顾虑是我希望它对用户友好,所以如果有多个问题,应该有多个输入框用于答案(每个问题一个)和一个提交按钮(所以多个答案可以一次发布)。

我认为我的方法很糟糕,但我不知道该怎么做,所以任何帮助都将不胜感激。

这是我目前所拥有的:

型号:

class Lesson < ActiveRecord::Base
  has_many :questions, dependent: :destroy
  has_many :answers, through: :questions
  accepts_nested_attributes_for :questions, reject_if: :all_blank, allow_destroy: true
  accepts_nested_attributes_for :answers, reject_if: :all_blank, allow_destroy: true
end

class Question < ActiveRecord::Base
  belongs_to :lesson
  has_many :answers, dependent: :destroy
end

class Answer < ActiveRecord::Base
  belongs_to :question
end

课长

class LessonsController < ApplicationController

  def show
    @questions = @lesson.questions
  end

  # PATCH/PUT /lessons/1
  # PATCH/PUT /lessons/1.json
  def update
    respond_to do |format|
      if @lesson.update(lesson_params)
        format.html { redirect_to @lesson, notice: 'Lesson was successfully updated.' }
        format.json { render :show, status: :ok, location: @lesson }
      else
        format.html { render :edit }
        format.json { render json: @lesson.errors, status: :unprocessable_entity }
      end
    end
  end     

 private

    def lesson_params
      params.require(:lesson).permit(:name,
      answers_attributes: [:id, :content, :question_id]
      )
    end
end

routes.rb

resources :lessons
  post '/lessons/:id', to: "lessons#update"

【问题讨论】:

  • 你应该看看处理嵌套表单的 Cocoon gem...
  • 您的代码正在中断,因为您没有在 before_action 中调用“set_lesson”方法。将before_action :set_lesson 放在控制器的顶部。
  • 该代码在控制器中,如果缺少,视图将不会显示显示视图。
  • 你没有发布它:)
  • 你试过这个simple_form_for question.answers.build而不是simple_form_for :answers吗?

标签: forms ruby-on-rails-4


【解决方案1】:

在 Gemfile 中添加 gem 并运行 bundle install:-

gem "nested_form"

在课程展示页面上:-

<%= nested_form_for @lession do |lession_form| %>
    <%= @lession.content %>        
    <%= lession_form.fields_for :questions do |question_form| %>
        <% @questions.each do |question| %>
            <%= question.content %><br /><br />
            <%= question_form.fields_for :answers do |answer_form| %>
                <%= answer_form.text_field :content %>
                <%= answer_form.link_to_remove "Remove this answer" %>
            <% end %>
            <%= question_form.link_to_add "Add more answer", :answers %>
        <% end %>
    <% end %>
    <%= lession_form.submit 'Update' %>
<% end %>

【讨论】:

  • 我不知道我是否正在尝试创建嵌套表单,我知道它可能看起来像我的代码,但就像我说的那样,我认为我的方法是错误的。我要做的就是为每个问题创建一个答案字段,并允许某人填充所有答案并使用 on 按钮提交。使用嵌套形式,它将允许上课者(即学生)更改不好的问题。
  • 在这里,我显示的是问题而不是问题的文本字段。因此,学生只能添加他们的答案。
  • 谢谢,我添加了代码进行小修正(将“课程”更改为“课程”。但是,表格现在似乎循环问题两次。问题循环(带有答案)为有问题很多次(即三个问题,它循环三次)。请注意,我也不需要 javascript 链接在这里添加更多记录(尽管我包含它来测试此代码)。
  • 我收到的另一个问题是“Answer(#54415320) expected, got Array(#7893924)” on submit。
【解决方案2】:

我原以为你可以在不使用宝石的情况下实现。

您可能需要在模型中指定 inverse_of。我之前发现在处理表单时,嵌套属性中需要这样做。

class Question < ActiveRecord::Base
  belongs_to :lesson
  has_many :answers, dependent: :destroy, :inverse_of => :question
end

class Lesson < ActiveRecord::Base
  has_many :questions, dependent: :destroy, :inverse_of => :lessons
  has_many :answers, through: :questions
  #etc.
end

在您的课程控制器中:

def show
  @lesson = Question.find(params[:id])
  @questions = @lesson.questions
  x.times { @lesson.questions.answer.build }
end 

在您的视图/课程/显示页面中:

<%= form_for @lesson do |lesson| %>
  <%= @lesson.whatever_attribute %>
  <%= lesson.fields_for :questions do |question| %>
    <% @questions.each do |question| %>
      <%= question.content %>
    <% end %>
      <div id="answers-div" class='form-group'>
        <%= question_form.fields_for :answers do |answer| %>
          <%= answer.text_field :content id:"answer-entry" %>
        <% end %>
      </div>
  <% end %>
<% end %>
<%= lesson.submit 'Submit' %>

在表单下方添加一些按钮以添加更多答案或删除:

<button class="btn btn-default" id="addNewAnswer">Add Another Answer Box</button><br>
<button class="btn btn-default" id="deleteNewAnswer">Delete Last Answer</button>

然后您可以使用 jQuery 即时添加和删除答案。

$(document).ready(function(){
  $("#addNewAnswer").click(function() {
    $("#answers-div").append(createNewInputElement($("#answers-div")));
  });
});

function createNewInputElement(form) {
  var newIndex = $("#answers-div").children('input#choice-entry').length;
  var newInput = $("#answer-entry").clone().attr('name', generateNewInputName(newIndex));
  newInput.val('');
  return newInput;
 };

 function generateNewInputName(idx) {
   return "question[answers_attributes][" + idx + "][content]"
 };


 $(document).ready(function(){
   $("#deleteNewAnswer").click(function() {
     if ($("#answers-div input").length > 1) {
       $("#answers-div input:last-child").remove();
     }
   });
 });

使用嵌套表单不是问题。您需要一个嵌套表单以允许您在 lessson.questions 中嵌套答案,但您只允许用户提供输入

如果使用 Rails 4(和 Strong 参数),您还需要按照这些方式允许这些(否则传递的参数将不被允许通过)。

private (in your Lessons controller)

def lesson_params
  params.require(:lesson).permit(:content, answer_attributes:[:content])
end

这可能并不完美,但我希望这是对您的问题的某种解决方案的开始。

【讨论】:

  • 感谢您的回答。是的,嵌套形式和您的建议可以使用 gem 完成 - 例如 Cocoon。我使用它让管理员能够创建课程和问题。我知道您没有显示该字段,但它似乎仍然存在漏洞(类似于此处描述的漏洞 Ryan Bates railscasts.com/episodes/…)。
  • 也许我理解错了,但我认为 Strong Params 会为您解决这个问题。正如“在被列入白名单之前,禁止在活动模型质量分配中使用此插件动作控制器参数。”我不确定以下方法是否有效,但如果是,我会认为它会解决您的问题。 params.require(:lesson).permit(answer_attributes:[:content])
  • 我正在尝试实现这一点,但遇到了一堆语法错误。我在表单中添加了一个缺少的 。它无法确定视图中的“question_form”是什么,因此我尝试将其更改为“课程”,但收到了相同的未定义变量。当我尝试将其更改为“问题”时,它给了我一个未定义方法“字段”的错误。我认为控制器操作不完整,所以我将 x 设置为@lesson.questions.count。模型更改导致可能找不到问题的反向关联(:问题中的课程)。
  • 嗨 - 我没有测试我的代码编辑器,所以为语法错误道歉。当您使用 &lt;%= lesson.fields_for :questions do |question| %&gt; 循环时,question_form 应该只是 question。另外,也许:inverse_of =&gt; :lesson rather than lessons.
  • 在阅读了inverse_of 上的这篇(viget.com/extend/…) 优秀帖子后,我认为这可能不是必需的,因为 1) 从 4.1 版开始,Rails 将尝试为您自动设置 :inverse_of 选项。 2) 在通过 :has_many 关联中的accepts_nested_attributes_for 创建对象及其子对象时需要它。在您的情况下,您已经创建了父对象,所以我会说它是不必要的。
【解决方案3】:

尝试测试此代码,再次给我发电子邮件 在你的课#show.html.erb

<% for question in @lesson.questions  %>
    <%= question.content %><br /><br />
    <%= simple_form_for :answers do |f| %>
      <%= f.input :content %>
      <%= f.hidden_field :question_id, :value => question.id %>
      <%= f.button :submit %>
    <% end %>
<% end %>

【讨论】:

  • 如果我希望每个问题使用一个表单,这可能会起作用,但如上所述,如果有多个问题,我想将它们放在一个表单中,这样对用户更友好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-21
  • 1970-01-01
  • 2022-11-07
  • 1970-01-01
相关资源
最近更新 更多