4

I am using Ruby on Rails 4 and I would like to properly handle the updating and creating process of associated records through the update_attributes method. That is, I have the following:

# Model
class Article < ActiveRecord::Base
  has_many :categories

  accepts_nested_attributes_for :categories
end

# Controller
class ArticlesController < ApplicationController
  def update
    @article = Article.find(params[:id])

    @article.update_attributes(update_params)
    ...
  end

  private

  def update_params
    params.require(:article).permit(:title, ..., :categories_attributes => [:name, ...])
  end
end

# Logger for the update request
Started PATCH "/articles/6"
Processing by ArticlesController#update
  Parameters: {"utf8"=>"✓", "article"=>{"title"=>"Sample title", "categories_attributes"=>{"0"=>{"name"=>"Sample Category 1", "..."=>"..."}, "1"=>{"name"=>"Sample Category 2", "..."=>"..."}, : "..." => {...}}
  ...

The issue is related to the way Rails handles things for updating associated records in the database, in my case when updating categories through the @article object: when data is submitted so to fire the update action and parameters are passed to the update_attributes method (as shown in the logger above) then Rails creates new category records in the database, one for each element present in the hash categories_attributes.

However, my intention is to update existing category records if those exist or create new ones if those do not exist, accordingly to the uniqueness of article_id and name columns in the categories database table, probabily performing a search for data present in these columns in order to check if it is needed to update or create new records. In fact, in the "edit article" view I display a form with input fields for editing categories including fields pre-populated with data related to existing categories and empty fields for categories that can be created "on the fly" by the editor user.

How can I properly handle this behavior? Is it model or controller responsability? Or, maybe, is there a better way to manage categories directly in the "edit article" view?

user502052
  • 14,803
  • 30
  • 109
  • 188

2 Answers2

0

I believe you need to permit the :id attribute for your nested model:

def update_params
  params.require(:article).permit(
    :title,
    ...,
    :categories_attributes => [:id, :name, ...]
  )
end

And if you want to be able to destroy categories with nested parameters, you'll need to add :_destroy as well. The Rails Guides have the full story.

lime
  • 6,901
  • 4
  • 39
  • 50
  • I read Rails Guides and [API Documentation](http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html) and I tried to use the `:id` but it didn't solve the issue, even for the updating/creating problem. Furthermore, I am asking myself: why is the `:id` necessary? I can just retrieve categories by running `@article.categories`. – user502052 Nov 28 '13 at 17:59
  • I'm a bit unclear on the details myself. :) Here's what I know: All parameters not explicitly listed in `permit` are dropped before being passed to `update_attributes`. If `:id` is missing at that stage there is no way of knowing which category should get updated, instead they all look like new categories. – lime Nov 28 '13 at 22:46
  • When I had similar issues I copied the raw parameters from the server log and compared them against what's left after `require` and `permit`. Maybe [these examples](https://github.com/rails/strong_parameters#use-outside-of-controllers) can help on using `strong_parameters` in the console. If you want, we can also keep digging in [chat](http://chat.stackoverflow.com/). – lime Nov 28 '13 at 23:04
  • I think that the problem is not related to StrongParameters... However, "we can also keep digging in chat". – user502052 Dec 01 '13 at 01:59
  • @user502052 I created [a room](http://chat.stackoverflow.com/rooms/42353/room-for-lime-and-user502052). There is [some confusion](http://meta.stackexchange.com/q/98926/209494) on whether moving to chat is a good idea, but I feel like if we reflect any breakthroughs here it will be fine. – lime Dec 02 '13 at 17:36
0

You should add categories_attributes as attr_accessible in your Article model. Like:

attr_accessible :categories_attributes

Without this line you are bound to face issues like above, creating new records instead of updating existing ones. Also there is a gem called nested_form which does an excellent job in building these complex forms.

ale
  • 6,369
  • 7
  • 55
  • 65
  • `attr_accessible` is [not used in Rails 4](http://stackoverflow.com/q/17371334/454094) by default, it is replaced by [Strong Parameters](http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters). Your answer is valid for Rails 3 and earlier though. – lime Dec 02 '13 at 17:49