How do I fetch HABTM related objects on a form after validation failed?
I have a very common HABTM relationship between Product and Category. I'm based on Railscasts Episode #17. The problem I'm facing is related to the way all the categories are fetched to show them in the product form (this is the way R. Bates has the view for it):
<% for category in Category.find(:all) %>
<div>
<%= check_box_tag "product[category_ids][]", category.id, @product.categories.include?(category) %>
<%= category.name %>
</div>
<% end %&g开发者_JS百科t;
but I want to implement it this way:
<% for category in @categories %>
where I have @categories
defined in my controller:
def edit
@categories = Categories.all
@product = Product.find params[:id]
end
It all goes smoothly until some validation fails. Say some field can't be blank and so the redirection (on the update action of the ProductsController
) shoot's me back to edit
:
def update
@product = Product.find(params[:id])
@supplier.category_ids = params[:product][:industry_category_ids] ||= []
respond_to do |format|
if @product.update_attributes(params[:product])
format.html { redirect_to @supplier, notice: 'Profile was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" } <=== HERE
format.json { render json: @supplier.errors, status: :unprocessable_entity }
end
end
end
An that point I get the following error, saying that @categories
in the correponding form is nil
:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.each
...
<% for ic in @industry_categories %> <==== HERE
...
So, is there a way to keep the MVC best practices to accomplish this? Or I just have to do it Bate's way?
In other words, is is possible to have a form_for
say @product
with checkboxes for the "HABTM" related objects and be redirected to it after validation fails BUT without fetching the stuff on the view (Category.all
) (i.e. doing it on the corresponding controller @categories = Category.all
as I showed before)
Thanks!
Populate @categories when you fail your validation:
def update
@product = Product.find(params[:id])
@supplier.category_ids = params[:product][:industry_category_ids] ||= []
respond_to do |format|
if @product.update_attributes(params[:product])
format.html { redirect_to @supplier, notice: 'Profile was successfully updated.' }
format.json { head :ok }
else
@categories = Categories.all <--- HERE
format.html { render action: "edit" }
format.json { render json: @supplier.errors, status: :unprocessable_entity }
end
end
end
When you make a call to "render :action", Rails just continues processing the current request and renders the view mapping to the :action you specified. So in this case, you're re-rendering the 'edit' view (because you failed your validation) but since there's no @categories variable declared in the 'update' action you will get a nil reference exception.
精彩评论