wie man ein Array mit starken Parametern zulässt

269

Ich habe eine funktionierende Rails 3-App, die has_many verwendet: durch Zuordnungen, die es mir nicht ermöglichen, IDs aus dem zugeordneten Modell in der Rails 4-Version zu speichern, da ich sie als Rails 4-App neu erstelle.

Dies sind die drei relevanten Modelle, die für die beiden Versionen gleich sind.

Categorization.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

In beiden Apps werden die Kategorie-IDs wie folgt an die Erstellungsaktion übergeben

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

Wenn ich in der Rails 3-App eine neue Frage erstelle, wird sie in die Fragentabelle und dann in die Kategorisierungstabelle eingefügt

 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]]

In der Rails 4-App wird nach der Verarbeitung der Parameter in QuestionController # create dieser Fehler in den Serverprotokollen angezeigt

Unpermitted parameters: category_ids

und die Frage wird nur in die Fragentabelle eingefügt

 (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

Obwohl ich die category_ids nicht im Questions-Modell speichere, setze ich category_ids als zulässigen Parameter im question_controller

   def question_params

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

Kann mir jemand erklären, wie ich die category_ids speichern soll? Beachten Sie, dass in der Datei category_controller.rb beider Apps keine Aktion zum Erstellen vorhanden ist.

Dies sind die drei Tabellen, die in beiden Apps gleich sind

 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

Aktualisieren

Dies ist die Erstellungsaktion aus der Rails 3-App

  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

Dies ist die Erstellungsaktion aus der Rails 4-App

   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

Dies ist die Methode 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
quelle
Wie sieht die Aktion zum Erstellen in beiden Apps aus?
Bennick
@bennick Ich habe die beiden Erstellungsaktionen hinzugefügt. Danke
Leahcim

Antworten:

527

Dieser https://github.com/rails/strong_parameters scheint der relevante Abschnitt der Dokumente zu sein:

Die zulässigen Skalartypen sind String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Datum, Uhrzeit, DateTime, StringIO, IO, ActionDispatch :: Http :: UploadedFile und Rack :: Test :: UploadedFile.

Um zu deklarieren, dass der Wert in params ein Array zulässiger Skalarwerte sein muss, ordnen Sie den Schlüssel einem leeren Array zu:

params.permit(:id => [])

In meiner App werden die category_ids an die create-Aktion in einem Array übergeben

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

Daher setze ich beim Deklarieren starker Parameter explizit category_ids als Array

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

Funktioniert jetzt perfekt!

( WICHTIG: Wie @Lenart in den Kommentaren feststellt, müssen sich die Array-Deklarationen am Ende der Attributliste befinden, da sonst ein Syntaxfehler angezeigt wird.)

Leahcim
quelle
105
Ich habe auch festgestellt, dass Deklarationen für Arrays am Ende der Attributliste stehen sollten. Ansonsten bekomme ich einen Syntaxfehlersyntax error, unexpected ')', expecting =>
Lenart
45
Der Grund, warum Array-Deklarationen (verschachtelte Parameter) am Ende stehen, ist, dass ActionController :: Parameters.permit erwartet, dass jedes Argument ein Hash oder ein Symbol ist. Ruby Magic verwandelt alle Schlüsselwertpaare s am Ende eines Methodenaufrufs in einen Hash, aber Ruby weiß nicht, was zu tun ist, wenn Sie in Ihrem Methodenaufruf Symbole mit Schlüssel / Wert-Paaren mischen.
Sameers
7
Wenn category_idses sich nicht um ein Array handelt (z. B. wurde ein String gesendet), werden die Schienen total verrückt und lösen ein aus. ERROR TypeError: expected Array (got String) for param `category_ids'Ist das ein Fehler in den Schienen? Bearbeiten: ja es ist: github.com/rails/rails/issues/11502
Dominik Goltermann
4
@ Lenart Danke, ich dachte ich würde verrückt. Dies sollte zumindest in der strong_params README
Sebastialonso
3
@Lenart können Sie es als Hash hinzufügen, wenn Sie Syntaxfehler vermeiden möchten. params.permit(:foo, { bar: [] }, :zoo).
Foo Bar Zoo
99

Wenn Sie ein Array von Hashes zulassen möchten (oder an array of objectsaus der Sicht von JSON)

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

2 Punkte, die hier zu beachten sind:

  1. arraysollte das letzte Argument der permitMethode sein.
  2. Sie sollten die Schlüssel des Hashs im Array angeben, da sonst ein Fehler Unpermitted parameter: arrayangezeigt wird, der in diesem Fall nur sehr schwer zu debuggen ist.
Brian
quelle
und Arrays von Arrays werden (noch) nicht unterstützt: github.com/rails/rails/issues/23640
Andrei Dziahel
11
arraymuss nicht am Ende sein, aber Sie müssen sicherstellen, dass Sie gültigen Ruby haben. params.permit(:foo, array: [:key1, :key2], :bar)wäre kein gültiger Ruby, da der Interpreter nach dem ersten Satz Hash-Schlüssel: Wert-Paare erwartet. Um gültigen Ruby zu haben, benötigen Sieparams.permit(:foo, {array: [:key1, :key2]}, :bar)
mastaBlasta
Du bist ein Champ!
Lollypop
22

Es sollte so sein

params.permit(:id => [])

Auch seit Rails Version 4+ können Sie verwenden:

params.permit(id: [])
Matias Seguel
quelle
12
Bitte nicht, dass das Array das letzte Argument der Genehmigungsmethode sein sollte
Matias Elorriaga
3
Diese Hash-Syntax, von der Sie sagten, dass sie für Rails v4 + gilt, ist tatsächlich eine in Ruby 1.9 und höher verfügbare Syntax, nicht das Rails-Framework (obwohl für Rails 4 Ruby 1.9.3 oder neuer erforderlich ist)
MegaTux
11

Wenn Sie eine Hash-Struktur wie diese haben:

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

Dann habe ich es so zum Laufen gebracht:

params.require(:link).permit(:title, time_span: [[:start, :end]])
Fremder
quelle
6

Ich kann noch keinen Kommentar abgeben, aber wenn Sie der Fellow Stranger-Lösung folgen, können Sie auch weiterhin verschachteln, falls Sie Schlüssel haben, deren Werte ein Array sind. So was:

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

Das funktioniert:

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