【问题标题】:Rails 4 deleting nested attributes, works on create but not on edit/updateRails 4删除嵌套属性,适用于创建但不适用于编辑/更新
【发布时间】:2014-07-05 09:10:13
【问题描述】:

所以我正在使用这个Railscast

而且我知道 Rails 4 中针对 Strong 参数进行了一些更改。

First relevant question.

Second relevant question

我已经四次检查了我的实现,但看不出哪里出错了。就目前而言,最初提交患者时勾选“销毁”框(即创建方法)按预期工作,并将删除任何具有复选框的药物并允许任何没有的药物(从三个表单输入它提供)。

但是,当我随后编辑该患者时,所有被检查删除的药物都会被复制(因此我最终得到的附加药物比我开始时更多),并且任何检查删除似乎没有改变。

因此,如果有两种药物附加了“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

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

感谢任何帮助,必须是我看不到的明显的东西。

编辑

改变这个...

    # 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

到这里……

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

似乎工作正常,所以我仍然确定这与强参数有关,但我确定后者不太安全,不是最佳实践。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-4 nested-attributes


    【解决方案1】:

    当你在做的时候

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

    它允许你的所有属性,不推荐。这更像是一种技巧,而不是一种解决方案。

    如果你看看你的问题

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

    如果您查看允许的参数

    # 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
    

    这将适用于创建患者,但不适用于更新或编辑他们,因为当您创建新记录时,它不需要您允许 id当您想要更新或编辑记录时您还需要允许其 id

    修复:

    只需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
    

    【讨论】:

    • @user2792268 很高兴我能帮上忙 :)
    • 请注意阅读本文的其他人,我必须允许药物属性:id,而不是患者属性。 params.require(:patient).permit(:first_name, :last_name,drugs_attributes: [:id, :name, :_destroy])
    • @user2792268 更新了我的答案。您应该允许患者 ID,否则在更新患者时会给您带来麻烦
    • @mandeep 如果您提供 :id 来创建它,它将更新该记录而不是创建新记录。这会造成一个安全漏洞,而强参数是专门用来防止的。
    • @Wit(旧但可能仍然与答案相关......)不,更新应在将剩余参数传递给单个、共享、强 params 语句之前删除 :id 参数。如果有人使用 :id 参数调用 Create,系统将更新该记录而不是创建新记录。您必须确保没有 :id 参数,否则任何允许访问 Create 的用户都可能修改任何任意记录。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    相关资源
    最近更新 更多