【问题标题】:Rails 6 nested attributes not deleting existing records when updatingRails 6嵌套属性在更新时不会删除现有记录
【发布时间】:2021-11-15 08:29:09
【问题描述】:

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

在更新问题记录时,有没有办法完全覆盖现有的嵌套参数,并用我们在更新请求中传入的参数替换?

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

问题.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

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-6.1


    【解决方案1】:

    如果我理解正确,您将通过单击选项旁边的按钮一一删除选项。这实际上不是您需要或想要使用嵌套属性的东西。嵌套属性仅在您同时创建/编辑多条记录时才相关。

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

    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)并清楚地传达了您正在做的事情。

    【讨论】:

    • > 这实际上不是您需要或想要使用嵌套属性的东西。实际上,我使用嵌套属性来存储属于问题的多个选项。因此,在编辑问题时,我需要从前端删除的选项以反映在数据库中。只有在提交表单时才需要删除选项
    • 执行此操作的标准方法是仅包含 _destroy 复选框。如果您想创建一个链接,您可以创建一个创建隐藏输入的事件处理程序。从 UI / UX 的角度来看,它没有任何意义。如果您有一个显示“删除此选项”的按钮,然后用户必须提交整个表单,那将非常令人困惑。
    • 最好只编写一个事件处理程序,发送DELETE /options/1 请求,然后在 AJAX 调用完成时删除输入。
    • 感谢您的输入,我目前已通过向选项对象添加 _destroy 键并仅显示将 _destroy 设置为 false 的键来实现它。
    【解决方案2】:

    我可以分享我最近的项目工作,这有点类似于我使用神社 gem 上传图像的地方,我可以更新/销毁与产品模型相关联的图像 产品.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]
    

    希望这可以帮助您解决您的问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      相关资源
      最近更新 更多