Rails 4 удаляет вложенные атрибуты, работает над созданием, но не при редактировании/обновлении

Итак, я работаю из этого Railscast.

И я знаю, что в Rails 4 произошли некоторые изменения для параметров Strong.

Первый актуальный вопрос.

Второй актуальный вопрос

Я проверил четвертую проверку своей реализации, но не вижу, где я ошибаюсь. Как и в настоящий момент, пометка "уничтожить" поле при отправке пациента изначально (т.е. Метод создания) работает по назначению и будет удалять любое лекарство, у которого есть флажок, и разрешать любое, что не имеет (из трех входных данных формы он обеспечивает).

Однако, когда я впоследствии отредактирую этого пациента, любые лекарства, которые не делают, проверяются для удаления, дублируются (поэтому я получаю больше приложенных лекарств, чем я начал), и все, что есть проверено на удаление, похоже, не меняется.

Итак, если есть две лекарства, прикрепленные "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

похоже, работает правильно, поэтому я все еще уверен, что это что-то связано с сильными параметрами, но последнее менее безопасно. Я уверен, а не лучшая практика.

Ответы

Ответ 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, но когда вы захотите для обновления или редактирования записи вам нужен ее идентификатор.

Fix:

Просто передайте атрибут 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