comment autoriser un tableau avec des paramètres forts

269

J'ai une application Rails 3 fonctionnelle qui utilise has_many: via des associations qui ne sont pas, comme je la refais comme une application Rails 4, me permettant d'enregistrer les identifiants du modèle associé dans la version Rails 4.

Ce sont les trois modèles pertinents qui sont les mêmes pour les deux versions.

Catégorisation.rb

class Categorization < ActiveRecord::Base

  belongs_to :question
  belongs_to :category
end

Question.rb

has_many :categorizations
has_many :categories, through: :categorizations

Category.rb

has_many :categorizations
has_many :questions, through: :categorizations

Dans les deux applications, les identifiants de catégorie sont transmis à l'action de création comme celle-ci

  "question"=>{"question_content"=>"How do you spell car?", "question_details"=>"blah ", "category_ids"=>["", "2"],

Dans l'application Rails 3, lorsque je crée une nouvelle question, elle s'insère dans le tableau des questions, puis dans le tableau des catégorisations

 SQL (82.1ms)  INSERT INTO "questions" ("accepted_answer_id", "city", "created_at", "details", "province", "province_id", "question", "updated_at", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)  [["accepted_answer_id", nil], ["city", "dd"], ["created_at", Tue, 14 May 2013 17:10:25 UTC +00:00], ["details", "greyound?"], ["province", nil], ["province_id", 2], ["question", "Whos' the biggest dog in the world"], ["updated_at", Tue, 14 May 2013 17:10:25 UTC +00:00], ["user_id", 53]]
  SQL (0.4ms)  INSERT INTO "categorizations" ("category_id", "created_at", "question_id", "updated_at") VALUES (?, ?, ?, ?)  [["category_id", 2], ["created_at", Tue, 14 May 2013 17:10:25 UTC +00:00], ["question_id", 66], ["updated_at", Tue, 14 May 2013 17:10:25 UTC +00:00]]

Dans l'application Rails 4, après avoir traité les paramètres dans QuestionController # create, je reçois cette erreur dans les journaux du serveur

Unpermitted parameters: category_ids

et la question est seulement insérée dans le tableau des questions

 (0.2ms)  BEGIN
  SQL (67.6ms)  INSERT INTO "questions" ("city", "created_at", "province_id", "question_content", "question_details", "updated_at", "user_id") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["city", "dd"], ["created_at", Tue, 14 May 2013 17:17:53 UTC +00:00], ["province_id", 3], ["question_content", "How's your car?"], ["question_details", "is it runnign"], ["updated_at", Tue, 14 May 2013 17:17:53 UTC +00:00], ["user_id", 12]]
   (31.9ms)  COMMIT

Bien que je ne stocke pas les category_ids sur le modèle Questions, j'ai défini category_ids comme paramètre autorisé dans le questions_controller

   def question_params

      params.require(:question).permit(:question_details, :question_content, :user_id, :accepted_answer_id, :province_id, :city, :category_ids)
    end

Quelqu'un peut-il expliquer comment je suis censé enregistrer les category_ids? Remarque, il n'y a pas d'action de création dans categories_controller.rb de l'une ou l'autre des applications.

Ce sont les trois tableaux qui sont les mêmes dans les deux applications

 create_table "questions", force: true do |t|
    t.text     "question_details"
    t.string   "question_content"
    t.integer  "user_id"
    t.integer  "accepted_answer_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "province_id"
    t.string   "city"
  end

 create_table "categories", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "categorizations", force: true do |t|
    t.integer  "category_id"
    t.integer  "question_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

Mettre à jour

Ceci est l'action de création de l'application Rails 3

  def create
      @question = Question.new(params[:question])
      respond_to do |format|
      if @question.save
        format.html { redirect_to @question, notice: 'Question was successfully created.' }
        format.json { render json: @question, status: :created, location: @question }
      else
        format.html { render action: "new" }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
end

Ceci est l'action de création de l'application Rails 4

   def create
      @question = Question.new(question_params)

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

Ceci est la méthode question_params

 private
    def question_params 
      params.require(:question).permit(:question_details, :question_content, :user_id, :accepted_answer_id, :province_id, :city, :category_ids)
    end
Leahcim
la source
À quoi ressemble l'action de création dans les deux applications?
bennick
@bennick J'ai ajouté les deux actions de création. Merci
Leahcim

Réponses:

527

Ce https://github.com/rails/strong_parameters ressemble à la section pertinente de la documentation:

Les types scalaires autorisés sont String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch :: Http :: UploadedFile et Rack :: Test :: UploadedFile.

Pour déclarer que la valeur dans params doit être un tableau de valeurs scalaires autorisées, mappez la clé sur un tableau vide:

params.permit(:id => [])

Dans mon application, les category_ids sont passés à l'action de création dans un tableau

"category_ids"=>["", "2"],

Par conséquent, lors de la déclaration de paramètres forts, j'ai explicitement défini category_ids pour être un tableau

params.require(:question).permit(:question_details, :question_content, :user_id, :accepted_answer_id, :province_id, :city, :category_ids => [])

Fonctionne parfaitement maintenant!

( IMPORTANT: comme le note @Lenart dans les commentaires, les déclarations de tableau doivent se trouver à la fin de la liste des attributs, sinon vous obtiendrez une erreur de syntaxe.)

Leahcim
la source
105
J'ai également remarqué que les déclarations pour les tableaux devraient être à la fin de la liste des attributs. Sinon, j'obtiens une erreur de syntaxesyntax error, unexpected ')', expecting =>
Lenart
45
La raison pour laquelle les déclarations de tableau (paramètres imbriqués) sont à la fin est que ActionController :: Parameters.permit attend que chaque argument soit un hachage ou un symbole. Ruby magic transformera toutes les paires clé / valeur à la fin d'un appel de méthode en un hachage, mais Ruby ne saura pas quoi faire si vous mélangez des symboles avec des paires clé / valeur dans votre appel de méthode.
même
7
si ce category_idsn'est pas un tableau (par exemple, une chaîne a été envoyée), alors rails devient complètement dingue et soulève ERROR TypeError: expected Array (got String) for param `category_ids'un problème. Edit: oui c'est: github.com/rails/rails/issues/11502
Dominik Goltermann
4
@Lenart Merci, je pensais que je devenais fou. Cela devrait au moins apparaître dans le fichier README de strong_params
Sebastialonso
3
@Lenart vous pouvez l'ajouter en tant que hachage si vous voulez éviter les erreurs de syntaxe. params.permit(:foo, { bar: [] }, :zoo).
Foo Bar Zoo
99

Si vous souhaitez autoriser un tableau de hachages (ou an array of objectsdu point de vue de JSON)

params.permit(:foo, array: [:key1, :key2])

2 points à noter ici:

  1. arraydevrait être le dernier argument de la permitméthode.
  2. vous devez spécifier les clés du hachage dans le tableau, sinon vous obtiendrez une erreur Unpermitted parameter: array, qui est très difficile à déboguer dans ce cas.
Brian
la source
et les tableaux de tableaux ne sont pas encore pris en charge: github.com/rails/rails/issues/23640
Andrei Dziahel
11
arrayn'a pas besoin d'être à la fin, mais vous devez vous assurer que vous avez un Ruby valide. params.permit(:foo, array: [:key1, :key2], :bar)ne serait pas un rubis valide car l'interpréteur attend des clés de hachage: paires de valeurs après le premier ensemble. Pour avoir un Ruby valide dont vous avez besoinparams.permit(:foo, {array: [:key1, :key2]}, :bar)
mastaBlasta
Tu es un champion!
Lollypop
22

Ça devrait être comme

params.permit(:id => [])

Depuis la version 4+ des rails, vous pouvez également utiliser:

params.permit(id: [])
Matias Seguel
la source
12
veuillez ne pas que le tableau soit le dernier argument de la méthode d'autorisation
Matias Elorriaga
3
Cette syntaxe de hachage que vous avez dit appliquer pour Rails v4 + est en fait une syntaxe disponible dans Ruby 1.9 et plus récent, pas le cadre Rails (bien que Rails 4 nécessite Ruby 1.9.3 ou plus récent)
MegaTux
11

Si vous avez une structure de hachage comme celle-ci:

Parameters: {"link"=>{"title"=>"Something", "time_span"=>[{"start"=>"2017-05-06T16:00:00.000Z", "end"=>"2017-05-06T17:00:00.000Z"}]}}

Alors voici comment je l'ai fait fonctionner:

params.require(:link).permit(:title, time_span: [[:start, :end]])
Fellow Stranger
la source
6

Je ne peux pas encore commenter, mais en suivant la solution Fellow Stranger, vous pouvez également continuer à imbriquer au cas où vous auriez des clés dont les valeurs sont un tableau. Comme ça:

filters: [{ name: 'test name', values: ['test value 1', 'test value 2'] }]

Cela marche:

params.require(:model).permit(filters: [[:name, values: []]])
Daniel Duque
la source