cómo permitir una matriz con parámetros fuertes
Tengo una aplicación Rails 3 en funcionamiento que usa has_many: a través de asociaciones, lo cual no es así, ya que la rehago como una aplicación Rails 4, lo que me permite guardar identificadores del modelo asociado en la versión Rails 4.
Estos son los tres modelos relevantes y son los mismos para las dos versiones.
Categorización.rb
class Categorization < ActiveRecord::Base
belongs_to :question
belongs_to :category
end
Pregunta.rb
has_many :categorizations
has_many :categories, through: :categorizations
Categoría.rb
has_many :categorizations
has_many :questions, through: :categorizations
En ambas aplicaciones, los identificadores de categoría se pasan a la acción de creación como esta
"question"=>{"question_content"=>"How do you spell car?", "question_details"=>"blah ", "category_ids"=>["", "2"],
En la aplicación Rails 3, cuando creo una nueva pregunta, se inserta en la tabla de preguntas y luego en la tabla de categorizaciones.
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]]
En la aplicación Rails 4, después de procesar los parámetros en QuestionController#create, aparece este error en los registros del servidor.
Unpermitted parameters: category_ids
y la pregunta solo se inserta en la tabla de preguntas
(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
Aunque no estoy almacenando los Category_ids en el modelo de Preguntas, configuro Category_ids como un parámetro permitido en el questions_controller.
def question_params
params.require(:question).permit(:question_details, :question_content, :user_id, :accepted_answer_id, :province_id, :city, :category_ids)
end
¿Alguien puede explicar cómo se supone que debo guardar los Category_IDs? Tenga en cuenta que no hay ninguna acción de creación en categorías_controller.rb de ninguna de las aplicaciones.
Estas son las tres tablas que son iguales en ambas apps
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
Actualizar
Esta es la acción de creación desde la aplicación 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
Esta es la acción de creación desde la aplicación 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
Este es el método 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
Este https://github.com/rails/strong_parameters parece la sección relevante de los documentos:
Los tipos escalares permitidos son String, Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile y Rack::Test::UploadedFile.
Para declarar que el valor en params debe ser una matriz de valores escalares permitidos, asigne la clave a una matriz vacía:
params.permit(:id => [])
En mi aplicación, los Category_ids se pasan a la acción de creación en una matriz.
"category_ids"=>["", "2"],
Por lo tanto, al declarar parámetros fuertes, configuro explícitamente categoría_ids para que sea una matriz
params.require(:question).permit(:question_details, :question_content, :user_id, :accepted_answer_id, :province_id, :city, :category_ids => [])
¡Funciona perfectamente ahora!
( IMPORTANTE: como señala @Lenart en los comentarios, las declaraciones de matriz deben estar al final de la lista de atributos; de lo contrario, obtendrá un error de sintaxis).
Si desea permitir una serie de hashes (o an array of objects
desde la perspectiva de JSON)
params.permit(:foo, array: [:key1, :key2])
2 puntos a tener en cuenta aquí:
array
debería ser el último argumento delpermit
método.- debe especificar las claves del hash en la matriz; de lo contrario, obtendrá un error
Unpermitted parameter: array
, que es muy difícil de depurar en este caso.
La matriz debe ser el último argumento del método de permiso.
params.permit(:id => [])
Además, desde Ruby 1.9 o posterior, puedes usar:
params.permit(id: [])
Si tiene una estructura hash como esta:
Parameters: {"link"=>{"title"=>"Something", "time_span"=>[{"start"=>"2017-05-06T16:00:00.000Z", "end"=>"2017-05-06T17:00:00.000Z"}]}}
Entonces así es como lo hice funcionar:
params.require(:link).permit(:title, time_span: [[:start, :end]])
cuando desee permitir múltiples campos de matriz, tendrá que enumerar los campos de matriz al final mientras lo permite, como se indica:
params.require(:questions).permit(:question, :user_id, answers: [], selected_answer: [] )
(esto funciona)