Rails 6 个嵌套属性在更新时不删除​​现有记录

Rails 6 nested attributes not deleting existing records when updating

我正在构建一个应用程序,我在其中使用嵌套属性在问题记录下存储不同的选项记录。有一个表单,用户可以在其中动态添加和删除选项。在我的创建操作中一切正常,但在更新操作中,如果我删除现有选项并提交表单,它不会从数据库中删除。

更新问题记录时,有什么办法可以完全覆盖已有的嵌套参数,替换为我们在更新请求中传递的参数吗?

我知道在属性中添加 _destroy 并将其作为参数传递可以满足我的要求。由于我在按下 UI 中的“删除”按钮时从我的前端状态中删除了选项信息,所以我没有将它与参数一起发送。 Rails 中是否有任何其他方法可以从控制器本身的更新操作中完全覆盖嵌套属性并删除那些未在更新请求中传递的嵌套记录?

question.rb

class Question < ApplicationRecord
  belongs_to :quiz
  has_many :options

  validates :body, presence: true
  accepts_nested_attributes_for :options
end

option.rb

class Option < ApplicationRecord
  belongs_to :question

  validates :body, presence: true
  validates :is_correct, inclusion: { in: [ true, false ], message: "must be true or false" }
end

questions_controller.rb

class QuestionsController < ApplicationController
 ...

 def update
   @question = Question.find_by(id: params[:id])
   if @question.update(question_params)
     render status: :ok, json: { notice: t("question.successfully_updated") }
   else
     render status: :unprocessable_entity, json: { error: @question.errors.full_messages.to_sentence }
   end
 end

...

private

  def question_params
    params.require(:question).permit(:body, :quiz_id, options_attributes: [:id, :body, :is_correct])
  end

Relevant question

如果我没理解错的话,您是通过单击选项旁边的按钮一个一个地删除选项。那实际上不是您需要或想要使用嵌套属性的东西。嵌套属性仅在您同时 creating/editing 多条记录时才相关。

虽然您可以通过更新父级来销毁单个嵌套记录:

patch '/questions/1', params: { 
  question: { options_attributes: [{ id: 1, _destroy: true }] }
}

它非常笨重,并不是一个好的 RESTful 设计。

相反,您可以设置标准 destroy 操作:

# config/routes.rb
resources :options, only: :destroy
<%= button_to 'Destroy option', option, method: :delete %>
class OptionsController < ApplicationController
  # @todo authenticate the user and 
  # authorize that they should be allowed to destroy the option
  # DELETE /options/1
  def destroy
    @option = Option.find(params[:id])
    @option.destroy
    respond_to do |format|
      format.html { redirect_to @option.question, notice: 'Option destroyed' }
      format.json { head :no_content }
    end
  end 
end

这使用了正确的 HTTP 动词(DELETE 而不是 PATCH)并清楚地传达了您正在做的事情。

我可以分享我最近的项目工作,这有点类似于我使用 shrine gem 上传图片的地方,我可以 update/destroy 与产品模型相关联的图片 product.rb

.
.
has_many :product_images, dependent: :destroy
accepts_nested_attributes_for :product_images, allow_destroy: true

product_image.rb

.
  belongs_to :product
.

_form.html.erb 更新

<%= f.hidden_field(:id, value: f.object.id) %>
<%= image_tag f.object.image_url unless f.object.image_url.nil? %>
<%= f.check_box :_destroy %>

并且在产品控制器中,我已将其列入白名单

product_images_attributes: [:_destroy,:image, :id]

希望这能帮助您解决问题