【问题标题】:Rails accepts_nested_attributes_for with belongs_to. Why I can't set id?Rails 接受_nested_attributes_for 和belongs_to。为什么我不能设置id?
【发布时间】:2018-11-29 10:39:00
【问题描述】:

我使用的是 Rails 5.1.6,但在 accept_nested_attributes_for 上遇到问题。

我有两个模型

class Material < ApplicationRecord
  belongs_to :rubric, optional: true
  accepts_nested_attributes_for :rubric
end

class Rubric < ApplicationRecord
  has_many :materials, dependent: :nullify
end

我尝试通过 rubric_attributes 将 rubric id 设置为新项目。

describe 'create material' do
  it 'should set rubric: :id' do
    # prepare
    item = FactoryBot.build(:material)
    rubric = FactoryBot.create(:rubric)

    # action
    item.assign_attributes(
      rubric_attributes: {
        id: rubric.id
      }
    )

    # check
    expect(item.valid?).to eq(true)
    expect(item.save).to eq(true)
    expect(item.rubric_id).to eq(rubric.id)
  end
end

但我有一个错误:

 Failure/Error:
   item.assign_attributes(
     rubric_attributes: {
       id: rubric.id
     }
   )

 ActiveRecord::RecordNotFound:
   Couldn't find Rubric with ID=1 for Material with ID=1

我在更新材料时遇到了同样的错误。

accepts_nested_attributes_for 的行为是可预测的,我不能使用 rubric_attributes 来设置现有的 rubric id?

【问题讨论】:

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


    【解决方案1】:

    Docs说:

    对于没有id 键的每个散列,将实例化一条新记录,除非该散列还包含评估为真的_destroy 键。

    它建议如果您在嵌套属性中传递id,它将被视为应该更新的现有记录。

    【讨论】:

      【解决方案2】:

      你很可能一开始就不需要accepts_nested_attributes_for

      如果您希望用户能够通过选择来选择记录,您实际上不需要做任何事情,除了创建选择并将material_id 属性列入白名单:

      <%= form_for(@material) do |f| %>
        <div class="field">
          <%= f.label :rubic_id %>
          <%= f.collection_select :rubic_id, Rubic.all :id, :name %>
        </div>
        <%= f.submit %>
      <% end %>
      

      选择将在参数中创建一个数组。

      class MaterialsController
        # POST /materials
        def create
          @material = Material.new(material_params)
          if @material.save
            redirect_to @material
          else
            render :new
          end
        end
      
        private
      
          def material_params
            params.require(:material)
                  .permit(:foo, :bar, material_ids: [])
          end
      end
      

      accepts_nested_attributes_for 真正适用于需要在同一个请求中创建/编辑嵌套资源的情况。您在这里使用它的唯一原因是:

      1. 用户应该能够以相同的形式创建材料。
      2. 您有一个连接表,其中包含您希望用户能够设置的其他属性(例如数量)。

      你仍然可以做 1. 连同上面的选择,但你不能使用accepts_nested_attributes_for 来设置一个简单的belongs_to 关联。你也不想像用火箭敲钉子一样。

      【讨论】:

        【解决方案3】:

        只是留下这个以防其他人可能像我一样遇到问题,通过 API 在 Rails 后端填充嵌套的子记录,但通过友好 ID 使用 hash_ids。

        在尝试通过 API 修补 Rails 记录时遇到此问题。 第一个设置是镜像 Rails 以嵌套形式发送记录值的方式。意思是,我特意构建了从前端发送到 Rails 后端的 params 哈希,就像典型的嵌套形式传输一样:

        { "children": {
              "0": {
                    "name": "foo",
                    "category_id": "1",
                    "hash_id": "HDJPQT"
              }
        }
        

        accepts_nested_attributes_for 需要id 来修补记录。否则它将完全创建一个新记录。在我的场景中我不想要。我发送了hash_id,因此无意中创建了新记录。

        解决方案

        目前我不再复制嵌套表单值哈希以发送到 Rails 后端。相反,我只是在来自 Javascript 前端的链式 fetch 查询中单独更新子记录。

        注意: 如果您想继续发送一个镜像嵌套形式的哈希数组,可以根据您的需要将数据库表的主键更改为 hash_id 或 UUID。尚未测试此解决方案。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-04
          • 1970-01-01
          • 2013-12-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多