ruby-on-rails - anidados - formularios en ruby on rails
Rieles: cómo rellenar el Id. De objeto padre utilizando atributos anidados para el objeto hijo y parámetros potentes. (1)
Entonces, el problema con el que se está encontrando es que los objetos secundarios no pasan la validación, ¿verdad? Cuando los objetos secundarios se crean al mismo tiempo que el elemento primario, es posible que los objetos secundarios no puedan conocer la identificación de sus padres para pasar la validación, es cierto.
Aquí es cómo puedes resolver ese problema. Cambie sus modelos de la siguiente manera:
# app/models/survey.rb
class Survey
belongs_to :user
has_many :questions, :inverse_of => :survey
accepts_nested_attributes_for :questions
end
# app/models/question.rb
class Question
validates :survey, :presence => true
belongs_to :survey
end
Las diferencias aquí son las siguientes :inverse_of
pasado a la asociación has_many
, y que la Question ahora valida en just :survey
lugar de :survey_id
.
:inverse_of
hace de modo que cuando un objeto secundario se crea o construye usando la asociación, también recibe una referencia retrospectiva al padre que lo creó. Esto parece algo que debería suceder automáticamente, pero desafortunadamente no lo hace a menos que especifiques esta opción.
Validar en :survey
lugar de en :survey_id
es una especie de compromiso. La validación ya no es simplemente verificar la existencia de algo que no esté en blanco en el campo survey_id; ahora realmente comprueba la asociación por la existencia de un objeto principal. En este caso, es conocido por :inverse_of
, pero en otros casos tendrá que cargar la asociación de la base de datos usando el id para validar. Esto también significa que los identificadores que no coinciden con nada en la base de datos no pasarán la validación.
Espero que ayude.
Tengo una situación muy parecida a la presentada en Railscast 196-197: Formulario de modelo anidado . Sin embargo, he encontrado un conflicto entre este enfoque y los parámetros fuertes. No puedo encontrar una buena forma de rellenar el campo de identificación de registro padre en el objeto hijo, ya que no quiero que se pueda asignar a través del formulario (para evitar que los usuarios asocien registros secundarios a registros principales que no son de su propiedad) ) Tengo una solución (vea el código a continuación) pero parece ser el tipo de cosas que Rails podría tener una manera inteligente y fácil de hacer por mí.
Aquí está el código ...
Hay un objeto principal (llámalo Encuesta) que tiene muchos objetos secundarios (llámalos Preguntas):
# app/models/survey.rb
class Survey
belongs_to :user
has_many :questions
accepts_nested_attributes_for :questions
end
# app/models/question.rb
class Question
validates :survey_id, :presence => true
belongs_to :survey
end
Hay un formulario que permite a los usuarios crear una encuesta y las preguntas de esa encuesta al mismo tiempo (para simplificar, el código siguiente trata las encuestas como si solo tuvieran una pregunta):
# app/views/surveys/edit.html.erb
<%= form_for @survey do |f| %>
<%= f.label :name %>
<%= f.text_field :name %><br />
<%= f.fields_for :questions do |builder| %>
<%= builder.label :content, "Question" %>
<%= builder.text_area :content, :rows => 3 %><br />
<% end %>
<%= f.submit "Submit" %>
<% end %>
El problema es el controlador. Quiero proteger el campo survey_id en el registro de preguntas a través de parámetros potentes, pero al hacerlo, las preguntas no pasan la validación, ya que survey_id es un campo obligatorio.
# app/controllers/surveys_controller.rb
class SurveysController
def edit
@survey = Survey.new
Survey.questions.build
end
def create
@survey = current_user.surveys.build(survey_params)
if @survey.save
redirect_to @survey
else
render :new
end
end
private
def survey_params
params.require(:survey).permit(:name, :questions_attributes => [:content])
end
end
La única forma en que puedo pensar para resolver este problema es crear las preguntas por separado de la encuesta como esta:
def create
@survey = current_user.surveys.build(survey_params)
if @survey.save
if params[:survey][:questions_attributes]
params[:survey][:questions_attributes].each_value do |q|
question_params = ActionController::Parameters.new(q)
@survey.questions.build(question_params.permit(:content))
end
end
redirect_to @survey
else
render :new
end
end
private
def survey_params
params.require(:survey).permit(:name)
end
(Rails 4 beta 1, Ruby 2)
ACTUALIZAR
Tal vez la mejor manera de manejar este problema es factorizar un "objeto de formulario" como se sugiere en esta publicación del blog Code Climate . Sin embargo, estoy dejando la pregunta abierta, ya que tengo curiosidad por otros puntos de vista