web-dev-qa-db-fra.com

Les attributs imbriqués RoR produisent des doublons lors de la modification

J'essaie de suivre Ryan Bates RailsCast # 196: Modèle imbriqué partie 1 . Il y a deux différences apparentes avec la version Ryans: 1) j'utilise un échafaudage intégré et pas astucieux comme il l'utilise, et 2) je lance Rails 4 (je ne le fais pas sais vraiment quelle version Ryans utilise dans son casting, mais ce n'est pas 4).

Voici donc ce que j'ai fait

Rails new survey2
cd survey2
bundle install
Rails generate scaffold survey name:string
rake db:migrate
Rails generate model question survey_id:integer content:text
rake db:migrate

Ensuite, j'ai ajouté les associations aux modèles comme ça

class Question < ActiveRecord::Base
  belongs_to :survey
end

et donc

class Survey < ActiveRecord::Base
  has_many :questions
  accepts_nested_attributes_for :questions
end

Ensuite, j'ai ajouté la partie vue imbriquée

<%= form_for(@survey) do |f| %>
  <!-- Standard Rails 4 view stuff -->

  <div class="field">
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </div>
  <div class="field">
    <%= f.fields_for :questions do |builder| %>
      <div>
        <%= builder.label :content, "Question" %><br/>
        <%= builder.text_area :content, :rows => 3 %>
      </div>
    <% end %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

et enfin le contrôleur pour que 3 questions soient créées chaque fois qu'une nouvelle enquête est instanciée

class SurveysController < ApplicationController
  before_action :set_survey, only: [:show, :edit, :update, :destroy]

  # Standard Rails 4 index and show 

  # GET /surveys/new
  def new
    @survey = Survey.new
    3.times { @survey.questions.build }
    Rails.logger.debug("New method executed")
  end

  # GET /surveys/1/edit
  def edit
  end

  # Standard Rails 4 create

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

  # Standard Rails 4 destroy

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_survey
      @survey = Survey.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def survey_params
      params.require(:survey).permit(:name, questions_attributes: [:content])
    end
end

Donc, créer une nouvelle enquête avec trois questions est très bien. Cependant, si j'essaie de modifier l'une des enquêtes, les trois questions d'origine sont conservées, tandis que trois autres supplémentaires sont créées. Donc, au lieu d'avoir 3 questions pour le sondage édité, j'en ai maintenant 6. J'ai ajouté

Rails.logger.debug("New method executed")

à la nouvelle méthode dans le contrôleur, et pour autant que je sache, elle n'est pas exécutée lorsque je fais une opération d'édition. Quelqu'un peut-il me dire ce que je fais mal?

Toute aide est grandement appréciée!

63
conciliator

Alors je l'ai compris. Je devais ajouter :id aux paramètres autorisés dans le survey_params méthode. Cela ressemble maintenant à ceci:

# Never trust parameters from the scary internet, only allow the white list through.
def survey_params
  params.require(:survey).permit(:name, questions_attributes: [:id, :content])
end

qui fonctionne parfaitement. Je suis un débutant RoR, alors veuillez prendre mon analyse avec un grain de sel, mais je suppose que ces nouveaux identifiants ont été générés au lieu d'être transmis à l'action de mise à jour. J'espère que cela aide quelqu'un d'autre là-bas.

155
conciliator

En utilisant cocoon gem sur Rails 4, je recevais toujours des champs en double même après avoir ajouté :id À la liste autorisée lors de la modification. Remarqué ce qui suit également

Unpermitted parameters: _destroy
Unpermitted parameters: _destroy

J'ai donc ajouté le champ :_destroy Au champ model_attributes: Autorisé et les choses se sont bien déroulées après cela.

Par exemple...

def survey_params
  params.require(:survey).permit(:name, questions_attributes: [:id, :content, :_destroy])
end
7
cevaris