Rails 3 rendering form with an array of nested attributes after failing validation
I have question model which has many options.
In my question controller new action I create five options ready for my user
def new
@question = Question.new
5.times.with_index do |index|
@question.options.build(:order => index)
end
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => @question }
end
end
In the view I loop through all options
- form_for(@question) do |f|
.field
= f.label :title, t("question.title")
= show_errors_for(@question, :title)
= f.text_field :title
- @question.options.each do |option|
- f.fields_for :options, option do |o|
.field
= o.label :option, t("question.option_no", { :index => option.order })
= o.text_field :option
= o.hidden_field :order, :value => option.order
.actions
= f.submit t("add_question.create")
My question model looks like this
class Question < ActiveRecord::Base
attr_accessible :title, :options_attributes
belongs_to :user
has_many :options
accepts_nested_attributes_for :options, :reject_if => proc { |attributes| attributes['option'].blank? }
validates :title, :length => { :maximum => 100 }, :presence => true
validate :min_no_of_options
def min_no_of_options
if self.options.size < 3
errors.add_to_base "Must have at least three options"
end
end
end
And my question controller create action
def create
if current_user
@question = current_user.questions.build(params[:question])
else
@question = Question.new(params[:question])
end
if @question.save
redirect_to(@question, :success => t('question.flash_success'))
else
flash.now[:error] = t("question.flash_error")
render :action => "new"
end
end
Now, when I enter only two options in the form and hit the create button the validation prevents the model from being saved. Which is good. But when the create action renders the new action again, only the option fields that I filled are showing up. The three option fields which were left blank have disappeared.
If I replace the "@question.save" in my create action with "false", the behavior is the same. So this suggests that something in the way I create the @question variable in the create action is responsible for throwing away my empty options.
But if I instead remove the :reject_if from my question model the empty options are showing up after a failing question save as expected. (I have a presence validation for the option attribute in my option model) So this tells me that there is nothing wrong in the way I create the @question variable in the create action. It is not throwing away the empty options. So where they are kicked out?
There was one pretty similar question, but the answer in there is not something I would like to do. Though it might be something I have to do. rails 开发者_开发知识库fields_for does not render after validation error on nested form
EDIT
After some more study with rails console I noticed that it truly is the creation of @question variable where the empty options get thrown away. This happens because I have the reject_if defined in the question model. After commenting the reject_if out from the model the empty options were added into the @question variable.
So I guess I need to remove the reject_if and use after_save callback to destroy empty options from the database. This way I will have the empty options going along with the question until the question is saved.
I'm answering my own question because I got the issue solved.
Blank "options" were removed from the "question" because of reject_if in the "question" model. The reject_if statement was applied when the code below was executed, and therefore blank "options" were removed.
@question = current_user.questions.build(params[:question])
I replaced the reject_if with an after_save callback which removes the options which were left blank.
精彩评论