Phil Aquilina
Phil Aquilina

Reputation: 939

Rails 3.1: Nested attributes won't save through the form

I suspect this might be a very simple mistake but I've spent 3 hours looking for it so I thought I might ask for some help from the community.

I'm running through Ryan Bates' excellent screencasts on Nested Models Forms and trying to apply them to my own project. The problem is the nested attribute doesn't seem to save using the form. I can get it to save through the console but it only shows up as empty brackets when going through the form.

Here's the relevant code:

The form view (using haml)

= form_for(@article) do |f|
  - if @article.errors.any?
    #error_explanation
      %h2
        = pluralize(@article.errors.count, "error")
        prohibited this article from being saved:
        %ul
          - @article.errors.full_messages.each do |msg|
            %li= msg
  .field
    = f.label :title
    %br/
    = f.text_field :title
  .field
    = f.label :intro
    %br/
    = f.text_area :intro
  = f.fields_for :subsections do |builder|
    = render 'subsections_fields', :f => builder
  .field
    = f.label :published_at
    %br/
    = f.text_field :published_at
  .actions
    = submit_or_cancel(f)

subsection_fields form view

= f.label :header
%br/
= f.text_field :header
= f.label :order_id
= f.number_field :order_id
%br/
= f.label :body
%br/
= f.text_area :body
%br/
= f.check_box :_destroy
= f.label :_destroy, "Remove Subsection"
%br/

Controller

class ArticlesController < ApplicationController
  def new
    @article = Article.new
    3.times { @article.subsections.build }
  end

  def create
    @article = Article.new(params[:article])

    if @article.save
      flash[:notice] = "Successfully created article."
      redirect_to @article
    else
      render :action => 'new'
    end
  end

  def edit
    @article = Article.find(params[:id])
  end

  def update
    @article = Article.find(params[:id])
    if @article.update_attributes(params[:article])
      flash[:notice] = "Successfully updated article."
      redirect_to @survey
    else
      render :action => 'edit'
    end
  end

  def destroy
    Article.find(params[:id]).destroy
    flash[:notice] = "Succesfully destroy article."
    redirect_to articles_url
  end

  def show
    @article = Article.find(params[:id])
  end

  def index
    @articles = Article.all
  end
end

And the models

class Article < ActiveRecord::Base
  attr_accessible :title, :intro

  has_many :subsections, :dependent => :destroy
  accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
                                              :allow_destroy => true
  has_and_belongs_to_many :categories
  validates :title, :presence => true
end


class Subsection < ActiveRecord::Base
  attr_accessible :header, :body, :order_id

  belongs_to :article

  validates :header,  :presence => true
  validates :body,    :presence => true 
end

Any help figuring this out is much appreciated.

Upvotes: 0

Views: 1756

Answers (3)

cmrichards
cmrichards

Reputation: 1772

Adding "attr_accessible" to a model changes the way mass assignment works in rails.

If you remove the "attr_accessible" lines in your models then all your code will work perfectly as it is.

The class method "accepts_nested_attributes_for" adds a "subsections_attributes=(value)" method to your model.

The second you add "attr_accessible" to a model you now are forced into adding extra "attr_accessible" entries for each field that you want to assign via mass assignment. i.e. when you use Article.new(params[:article]).

I hope that was clear.

Upvotes: 1

Phil Aquilina
Phil Aquilina

Reputation: 939

I figured out the answer to this one from another question.

The answer was to set my subsections_attributes as an attr_accessible, so the above Article model should look like this (I also added published_at as an attr_accessible):

class Article < ActiveRecord::Base
  attr_accessible :title, :intro, :subsections_attributes, :published_at

  has_many :subsections, :dependent => :destroy
  accepts_nested_attributes_for :subsections, :reject_if => lambda { |a| a[:body].blank? },
                                              :allow_destroy => true
  has_and_belongs_to_many :categories
  validates :title, :presence => true
end

Upvotes: 0

Vapire
Vapire

Reputation: 4588

I'm not quite sure, but try it with attr_accessible :article_id as well in your Subsection model?

Upvotes: 1

Related Questions