Rails 4删除嵌套属性,可用于创建,但不能用于编辑/更新

时间:2021-09-19 01:16:36

So I'm working from this Railscast.

我在轨道广播公司工作。

And I'm aware that there have been some changes in Rails 4 for Strong parameters.

我知道Rails 4对于强参数有一些变化。

First relevant question.

第一个相关的问题。

Second relevant question

第二个相关的问题

I've quadruple checked my implementation, but can't see where I'm going wrong. As it is at the moment, ticking the "destroy" box when submitting the patient initially (i.e. the create method) works as intended, and will delete any medication that has a checked box and permitting any that does not (from the three form inputs it provides).

我检查了我的实现四倍,但是不知道哪里出错了。就像目前的情况一样,在最初提交病人的时候勾选“销毁”框(即创建方法),并将删除任何有复选框的药物,并允许任何没有的药物(从它提供的三个表单输入中)。

However when I subsequently edit that patient, any medications that don't get checked to be deleted are duplicated (so I end up with more attached medications than I started with), and any that are checked for deletion don't seem to change.

然而,当我随后编辑那个病人时,任何未经检查而被删除的药物都是重复的(因此我最终得到的附加药物比开始时更多),任何被检查为删除的药物似乎都不会改变。

So if there two medicines attached "Med1" and "Med2", and I edit the patient, if both are marked for deletion I will still end up with "Med1" and "Med2". If only "Med1" is marked for deletion I will end up with "Med1" and "Med2" and an extra "Med2". If neither are marked for deletion I will end up with two each of "Med1" and "Med2".

因此,如果有两种药物附着在“Med1”和“Med2”上,并且我对患者进行编辑,如果两者都标记为删除,那么我仍然会以“Med1”和“Med2”结尾。如果只将“Med1”标记为删除,我将以“Med1”、“Med2”和一个额外的“Med2”结束。如果两者都没有被标记为删除,我将以“Med1”和“Med2”的两个结果结束。

#patient.rb
class Patient < ActiveRecord::Base
has_many :procedures
has_many :medications, dependent: :destroy
has_many :previous_operations, dependent: :destroy

accepts_nested_attributes_for :medications, :allow_destroy => true, :reject_if => lambda { |a| a[:name].blank? },
end

#views/patients/_form.html.erb
<%= form_for(@patient) do |f| %>
  <% if @patient.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@patient.errors.count, "error") %> prohibited this patient from being saved:</h2>

      <ul>
      <% @patient.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <%= f.fields_for :medications do |builder| %>
    <%= render "medication_fields", :f => builder %>
  <% end %>

  <div class="field">
    <%= f.label :first_name %><br>
    <%= f.text_field :first_name %>
  </div>
  <div class="field">
    <%= f.label :last_name %><br>
    <%= f.text_field :last_name %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

#views/patients/medications_fields.html
<div class="field">
  <%= f.label :name %><br>
  <%= f.text_field :name %>
</div>
<div class="field">
  <%= f.label :_destroy, "Remove Medication" %>
  <%= f.check_box :_destroy %>
</div>

#controllers/patients_controller.rb
class PatientsController < ApplicationController
  before_action :set_patient, only: [:show, :edit, :update, :destroy]

  # GET /patients
  # GET /patients.json
  def index
    @patients = Patient.all
  end

  # GET /patients/1
  # GET /patients/1.json
  def show
  end

  # GET /patients/new
  def new
    @patient = Patient.new
    3.times { @patient.medications.build }
  end

  # GET /patients/1/edit
  def edit
  end

  # POST /patients
  # POST /patients.json
  def create
    @patient = Patient.new(patient_params)

    respond_to do |format|
      if @patient.save
        format.html { redirect_to @patient, notice: 'Patient was successfully created.' }
        format.json { render action: 'show', status: :created, location: @patient }
      else
        format.html { render action: 'new' }
        format.json { render json: @patient.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /patients/1
  # PATCH/PUT /patients/1.json
  def update
    respond_to do |format|
      if @patient.update(patient_params)
        format.html { redirect_to @patient, notice: 'Patient was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: 'edit' }
        format.json { render json: @patient.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /patients/1
  # DELETE /patients/1.json
  def destroy
    @patient.destroy
    respond_to do |format|
      format.html { redirect_to patients_url }
      format.json { head :no_content }
    end
    flash[:notice] = "Patient was successfully deleted."
  end

  private

    # Never trust parameters from the scary internet, only allow the white list through.
    def patient_params
      params.require(:patient).permit(:first_name, :last_name, medications_attributes: [:name, :_destroy])
    end

end

I've been super careful and checked over a million times the :_destroy flag being allowed through strong parameters, but still no dice.

我已经非常小心地检查了超过一百万次:_destroy标志被允许通过强参数,但是仍然没有骰子。

Any help appreciated, must be something obvious that I just can't see.

任何帮助都是显而易见的,我就是看不见。

EDIT

编辑

Changing this...

改变这种……

    # Never trust parameters from the scary internet, only allow the white list through.
    def patient_params
      params.require(:patient).permit(:first_name, :last_name, medications_attributes: [:name, :_destroy])
    end

To this...

这……

    # Never trust parameters from the scary internet, only allow the white list through.
    def patient_params
      params.require(:patient).permit!
    end

appears to work correctly, so I'm still sure it's something to do with strong parameters, but the latter is less secure I'm sure and not best practice.

看起来是正确的,所以我仍然确信这与强参数有关,但我确信后者不那么安全,也不是最佳实践。

1 个解决方案

#1


33  

when you are doing

当你正在做的事情

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit!
end

it permits all of your attributes and it's not recommended. It's more of a hack rather than a solution.

它允许您的所有属性,但不建议这样做。这与其说是解决方案,不如说是一种技巧。

If you look at your question

如果你看看你的问题

Rails 4 deleting nested attributes, works on create but not on edit/update

Rails 4删除嵌套属性,可用于创建,但不能用于编辑/更新

and if you look at your params permitted

如果你看看允许的参数

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit(:first_name, :last_name, medications_attributes: [:name, :_destroy])
end

This will work on creating patients but not on updating or editing them because when you create a new record it doesn't require you to permit id but when you want to update or edit a record you need its id to be permitted as well.

这将用于创建患者,而不是更新或编辑患者,因为当您创建一个新记录时,它不需要您允许id,但是当您想要更新或编辑一个记录时,您也需要允许它的id。

Fix:

解决办法:

Just pass the id attribute to permitted attributes and it will work for you

只要将id属性传递给允许的属性,它就会为您工作

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit(:id, :first_name, :last_name, medications_attributes: [:id,:name, :_destroy])
end

#1


33  

when you are doing

当你正在做的事情

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit!
end

it permits all of your attributes and it's not recommended. It's more of a hack rather than a solution.

它允许您的所有属性,但不建议这样做。这与其说是解决方案,不如说是一种技巧。

If you look at your question

如果你看看你的问题

Rails 4 deleting nested attributes, works on create but not on edit/update

Rails 4删除嵌套属性,可用于创建,但不能用于编辑/更新

and if you look at your params permitted

如果你看看允许的参数

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit(:first_name, :last_name, medications_attributes: [:name, :_destroy])
end

This will work on creating patients but not on updating or editing them because when you create a new record it doesn't require you to permit id but when you want to update or edit a record you need its id to be permitted as well.

这将用于创建患者,而不是更新或编辑患者,因为当您创建一个新记录时,它不需要您允许id,但是当您想要更新或编辑一个记录时,您也需要允许它的id。

Fix:

解决办法:

Just pass the id attribute to permitted attributes and it will work for you

只要将id属性传递给允许的属性,它就会为您工作

# Never trust parameters from the scary internet, only allow the white list through.
def patient_params
  params.require(:patient).permit(:id, :first_name, :last_name, medications_attributes: [:id,:name, :_destroy])
end