【问题标题】:Rails nested form with accepts_nested_attributes_for and belongs_to带有accepts_nested_attributes_for 和belongs_to 的Rails 嵌套表单
【发布时间】:2021-09-20 19:02:53
【问题描述】:

我正在 Rails (5.2.6) 中构建一个表单,我想使用 accepts_nested_attributes_for,就像在 this question 中一样。但是,该问题的答案不起作用,我怀疑这是因为我的模型已经 belongs_to 另一个模型。

背景 本质上,我正在存储 AB 测试的结果。我想要实现的是让客户拥有他们跟踪测试的独特指标的能力。 Metrics 模型允许用户识别要跟踪的额外方面,而 Outcomes 旨在存储该数据。

我的尝试 我想在记录结果的同时记录结果;我的方法是在测试模型上添加accepts_nested_attributes_for :outcomes,但我不断收到Unpermitted parameter: :outcome 错误。

这是我的模型:

class Client < ApplicationRecord
    has_many :tests, dependent: :destroy
    has_many :metrics, dependent: :destroy
    validates :name, presence: true
end

class Metric < ApplicationRecord
  belongs_to :client
  has_many :outcomes, dependent: :destroy
  has_many :tests, through: :outcomes

  validates :name, presence: true
  validates :data_type, presence: true
end

class Test < ApplicationRecord
  belongs_to :client
  has_many :results
  has_many :outcomes
  has_many :metrics, through: :outcomes

  accepts_nested_attributes_for :outcomes, allow_destroy: true

  validates :name, presence: true
end

class Result < ApplicationRecord
  belongs_to :test
end

class Outcome < ApplicationRecord
  belongs_to :metric
  belongs_to :test

  validates :result, presence: true
end

和(一些)我的控制器:

class TestsController < ApplicationController
  def show
    @client = Client.find(params[:client_id])
    @test = @client.tests.find(params[:id])
  end

  def new
    @client = Client.find(params[:client_id])
    @test = @client.tests.new
    @test.build_outcome
  end
  
  def create
    @client = Client.find(params[:client_id])
    @test = @client.tests.create(test_params)
    redirect_to client_path(@client)
  end

  def edit
    @client = Client.find(params[:client_id])
    @test = @client.tests.find(params[:id])
  end
  
  def update
    @client = Client.find(params[:client_id])
    @test = @client.tests.find(params[:id])

    if @test.update(test_params)
      redirect_to client_test_path(@client, @test)
    else
      render 'edit'
    end
  end

  def destroy
    @client = Client.find(params[:client_id])
    @test = @client.tests.find(params[:id])
    @test.destroy
    redirect_to client_path(@client)
  end
  
  
  private
    def test_params
      params.require(:test).permit(:name, outcome_attributes: [:result])
    end
end

class OutcomesController < ApplicationController
  def show
    @test = Test.find(params[:test_id])
    @outcome = @test.outcome.find(params[:id])
  end
  
  def create
    @test = Test.find(params[:test_id])
    @outcome = @test.outcomes.create(outcome_params)
    redirect_to test_path(@test)
  end

  def edit
    @test = Test.find(params[:test_id])
    @outcome = @test.outcomes.find(params[:id])
  end
  
  def update
    @test = Test.find(params[:test_id])
    @outcome = @test.outcomes.find(params[:id])

    if @outcome.update(outcome_params)
      redirect_to test_outcome_path(@test, @outcome)
    else
      render 'edit'
    end
  end

  def destroy
    @test = test.find(params[:test_id])
    @outcome = @test.outcomes.find(params[:id])
    @outcome.destroy
    redirect_to test_path(@test)
  end
  
  
  private
    def outcome_params
      params.require(:outcome).permit(:test)
    end
end

还有形式:

<%= form_with model: [ @test, @test.results.build ] do |form| %>
  <p>
    <%= form.label :participants %><br>
    <%= form.number_field :participants %>
  </p>
  <p>
    <%= form.label :completed %><br>
    <%= form.number_field :completed %>
  </p>
  <p>
    <%= form.label :confidence %><br>
    <%= form.number_field :confidence %>
  </p>

  <% if @test.client.metrics.count > 0 %>
    <%= form.fields_for :outcome do |a| %>

      <% @test.client.metrics.each do |m| %>
        <p>
          <%= a.label m.name %><br>
          <%= a.number_field :result %>
        </p>
      <% end %>

      
    <% end %>
   <% end %>

  <p>
    <%= form.submit %>
  </p>
<% end %>

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-5


    【解决方案1】:

    它的两个简单的复数错误。

    params.require(:test)
          .permit(:name, outcomes_attributes: [:result])
    

    The param naming scheme for has_many is plural_attributes.

    对于fields_for,您需要使用与对象上的方法相同的名称:

    <%= form.fields_for :outcomes do |a| %>
      ...
    <% end %>
    

    如果您想提交带有嵌套结果的测试,您也使用了错误的路线:

    <%= form_with model: @test do |form| %>
    

    不要在form_with 的参数中使用.new 或其别名.build。它可以防止代码重用,并且会清除任何用户输入,因为您总是将表单绑定到新记录。在控制器中构建/获取记录并将它们传递给视图。

    另外注意&lt;% if @test.client.metrics.count &gt; 0 %&gt;count 总是创建一个额外的数据库查询。请改用#size#any? 来避免这种情况。

    【讨论】:

    • 除了这些错误之外,您还有很多代码重复、冗余和损坏的代码,例如 OutcomesController#create 和 validates :result, presence: true。它也很不清楚为什么你有两个在语义上非常相似的名字OutcomeResult,这势必会导致混淆。
    • 我按照你说的更新了它,但我得到了同样的错误。 (感谢.count 的解释;我换了。)我同意OutcomeResult 太相似了,但在我将我的解决方案应用于我的实际项目之前,这是一个测试应用程序,所以我并不过分关心他们的名字。
    • 我觉得我所指出的内容实际上只是冰山一角。创建一个更简单的示例,然后首先弄清楚嵌套属性是如何工作的 - 无论如何它已经足够复杂了,这段代码看起来就像你正在尝试进行多层嵌套,这是非常高级的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-07
    • 2010-11-16
    • 1970-01-01
    相关资源
    最近更新 更多