开发者

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 @categoriesdefined 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜