【问题标题】:Ruby on Rails - Problem with nested_attributes validation when allow_destroy: trueRuby on Rails - allow_destroy 时的嵌套属性验证问题:true
【发布时间】:2021-04-16 21:49:01
【问题描述】:

我有一些accepts_nested_attributes_for的模型

class SomeModel < ApplicationRecord
  has_many :some_resources
  
  accepts_nested_attributes_for :some_resources, allow_destroy: true
    
  ...

在 View 中看起来是这样的

<%= form_with(model: @some_model, ...) do |form| %>
...
<%= form.fields_for :some_resources do |some_resource_form| %>
  <table>
    <tbody>  
    <%= some_resource_form.hidden_field :_destroy, value: '0' %>
    <tr>
      <th>
        <%= some_resource_form.label :some_field_1, 'Some field 1' %>
      </th>
      <td>
        <%= some_resource_form.text_field :some_field_1 %>
      </td>
    </tr>
    <tr>
      <th>
        <%= some_resource_form.label :some_field_2, 'Some field 2' %>
      </th>
      <td>
        <%= some_resource_form.text_field :some_field_2 %>
      </td>
    </tr>
    </tbody> 
  </table>
<% end %>
...
<% end %>

在控制器中

def new
  3.times do |i|
    @some_model.some_resources.build
  end
end

配置

...
config.active_record.index_nested_attribute_errors = true

绘制页面时是这样的

<table>
  <tbody>
    <input value="0" type="hidden" name="some_model[some_resources_attributes][0][_destroy]" id="some_model_some_resources_attributes_0__destroy">

    <tr>
      <th>
        <label for="some_model_some_resources_attributes_0_some_field_1">Some field 1</label>
      </th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][0][some_field_1]" id="some_model_some_resources_attributes_0_some_field_1">
      </td>
    </tr>

    <tr>
      <th><label for="some_model_some_resources_attributes_0_some_field_2">Some field 2</label></th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][0][some_field_2]" id="some_model_some_resources_attributes_0_some_field_2">
      </td>
    </tr>
  </tbody>
</table>  

<table>
  <tbody>
    <input value="0" type="hidden" name="some_model[some_resources_attributes][1][_destroy]" id="some_model_some_resources_attributes_1__destroy">

    <tr>
      <th>
        <label for="some_model_some_resources_attributes_1_some_field_1">Some field 1</label>
      </th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][1][some_field_1]" id="some_model_some_resources_attributes_1_some_field_1">
      </td>
    </tr>

    <tr>
      <th><label for="some_model_some_resources_attributes_1_some_field_2">Some field 2</label></th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][1][some_field_2]" id="some_model_some_resources_attributes_1_some_field_2">
      </td>
    </tr>
  </tbody>
</table>   

<table>
  <tbody>
    <input value="0" type="hidden" name="some_model[some_resources_attributes][2][_destroy]" id="some_model_some_resources_attributes_2__destroy">

    <tr>
      <th>
        <label for="some_model_some_resources_attributes_2_some_field_1">Some field 1</label>
      </th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][2][some_field_1]" id="some_model_some_resources_attributes_2_some_field_1">
      </td>
    </tr>

    <tr>
      <th><label for="some_model_some_resources_attributes_2_some_field_2">Some field 2</label></th>
      <td>
        <input type="text" name="some_model[some_resources_attributes][2][some_field_2]" id="some_model_some_resources_attributes_2_some_field_2">
      </td>
    </tr>
  </tbody>
</table>      

存在使用_destroy 属性仅创建选定对象的能力。 触发验证时,errors 具有以下结构

#<ActiveModel::Errors:0x00007f4f695a5720 @base=#<SomeModel id: nil, label: "Label", ..., created_at: nil, updated_at: nil, deleted_at: nil>,
@messages={
:"some_resources[0].some_field_1"=>["can't be empty"], :"some_resources[0].some_field_2"=>["can't be empty"],
:"some_resources[1].some_field_1"=>["can't be empty"], :"some_resources[1].some_field_2"=>["can't be empty"], 
:"some_resources[2].some_field_1"=>["can't be empty"], :"some_resources[2].some_field_2"=>["can't be empty"]
}, 
@details={
:"some_resources[0].some_field_1"=>[{:error=>:blank}], :"some_resources[0].some_field_2"=>[{:error=>:blank}], 
:"some_resources[1].some_field_1"=>[{:error=>:blank}], :"some_resources[1].some_field_2"=>[{:error=>:blank}], 
:"some_resources[2].some_field_1"=>[{:error=>:blank}], :"some_resources[2].some_field_2"=>[{:error=>:blank}]
}>

例如,当我不想保存第二个对象(带有字段name="some_model[some_resources_attributes][1][some_field_1]")时,就会出现问题。为此,我为参数_destroy 设置了值“1”。之后,errors 包含以下内容

#<ActiveModel::Errors:0x00007f4f627d4d18 @base=#<SomeModel id: nil, label: "Label", ..., created_at: nil, updated_at: nil, deleted_at: nil>,
@messages={
:"some_resources[0].some_field_1"=>["can't be empty"], :"some_resources[0].some_field_2"=>["can't be empty"],
:"some_resources[1].some_field_1"=>["can't be empty"], :"some_resources[1].some_field_2"=>["can't be empty"]
}, 
@details={
:"some_resources[0].some_field_1"=>[{:error=>:blank}], :"some_resources[0].some_field_2"=>[{:error=>:blank}], 
:"some_resources[1].some_field_1"=>[{:error=>:blank}], :"some_resources[1].some_field_2"=>[{:error=>:blank}]
}>

可以看出,嵌套属性的错误只包含errors中对象的序号。 因此场 name="some_model[some_resources_attributes][0][some_field_1]" 错误索引为 0 错误中缺少name="some_model[some_resources_attributes][1][some_field_1]" name="some_model[some_resources_attributes][2][some_field_1]" 错误索引为 1

由于这种行为不可能在正确的字段中绘制错误消息。 我认为这个问题很普遍。但我什么也没找到。是否有任何解决方案以及存在哪些更好的实践来实现这样的功能? 谢谢!

附:我使用 Rails 6.0.3.6

【问题讨论】:

    标签: ruby-on-rails validation nested-attributes


    【解决方案1】:

    这是一种奇怪的行为。我的解决方案可能是添加自定义错误。根据 Rails 文档https://guides.rubyonrails.org/active_record_validations.html#working-with-validation-errors,您可以在验证中添加自定义错误,add 方法通过获取属性、错误类型和附加选项哈希来创建错误对象。

    在您的模型中:

    attr_accessor :input_field_index
    
    validate do |resource|
      errors.add :attribute_name, :custom_error_name, message: "is blank", options: {input_field_index: 2}
    end
    

    在您的视图中添加一个带有输入字段索引的 hidden_​​field。还记得在你的控制器的 strong_params 中允许它。

    您应该能够使用 input_field_index 参数获取错误,然后您可以在视图中对其进行自定义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2011-05-25
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多