开发者

Passing params to a new template on a failed create action when using accepts_nested_attributes_for

I may just be missing something simple, but I am relatively inexperienced so it is likely. I've searched extensively for a solution without success.

I am using the fields_for function to build a nested form using the accepts_nested_attributes_for function. If the submit on the form fails the params are passed to the render of the new template only for the parent model. How do I pass the nested params for the child model so that fields that have been filled out previously remain filled. Note that I am using simple_form and HAML but I assume this shouldn't impact the solution greatly.

My models:

class Account < ActiveRecord::Base
  attr_accessible :name
  has_many :users,  :dependent => :destroy
  accepts_nested_attributes_for :users, :reject_if => proc { |a| a[:email].blank? }, :allow_destroy => true
end

class User < ActiveRecord::Base
  attr_accessible :email, :password, :password_confirmation
  belongs_to :account
end

My accounts controller:

def new
  @account = Account.new
  @account.users.build
end

def create
  @account = Account.new(params[:account])
  if @account.save
    flash[:success] = "Welcome."
    redirect_to @account
  else
    @account.users.build
                           <- I suspect I need something here but unsure what
    render :new
  end
end

The key part of the accounts/new view:

= simple_form_for @account do |f|
  = f.input :name
  = f.simple_fields_for :users do |u|
    = u.input :email
    = u.input :password
    = u.input :password_confirmation
  = f.button :submit, :value => "Sign up"

My params on a failed save are:

:account    {"name"=>"In", "users_attributes"=>{"0"=>{"email"=>"u@e.com", "password"=>"pass", "password_confirmation"=>"pass"}}}

As you can see, the key information, in the users_attributes section, is stored but I can't seem to have the email address default i开发者_如何学编程nto the new form. Account name on the other hand is filled automatically as per Rails standard. I'm not sure if the solution should live in the accounts controller or in the accounts/new view, and have not had any luck with either.

Answers with .erb are, of course, fine.

I'm fairly new to Ruby and Rails so any assistance would be much appreciated.


The problem lies with attr_accessible, which designates the only attributes allowed for mass assignment.

I feel a bit silly in that I actually stated the problem in a comment last night and failed to notice:

accepts_nested_attributes_for :users will add a users_attributes= writer to the account to update the account's users.

This is true, but with attr_accessible :name, you've precluded every attribute but name being mass-assigned, users_attributes= included. So when you build a new account via Account.new(params[:account]), the users_attributes passed along in params are thrown away.

If you check the log you might note this warning:

WARNING: Can't mass-assign protected attributes: users_attributes

You can solve your original problem by adding :users_attributes to the attr_accessible call in the account class, allowing it to be mass-assigned.


Amazingly, after reading a blog post this evening, and some more trial and error, I worked this out myself.

You need to assign an @user variable in the 'new' action so that the user params are available for use in the 'create' action. You then need to use both the @account and @user variables in the view.

The changes look like this.

Accounts Controller:

def new
  @account = Account.new
  @user = @account.users.build
end

def create
  @account = Account.new(params[:account])
  @user = @account.users.build(params[:account][:user]
  if @account.save
    flash[:success] = "Welcome."
    redirect_to @account
  else        
    render :new
  end
end

The accounts/new view changes to:

= simple_form_for @account do |f|
  = f.input :name
  = f.simple_fields_for [@account, @user] do |u|
    = u.input :email
    = u.input :password
    = u.input :password_confirmation
  = f.button :submit, :value => "Sign up"

In this case the params remain nested but have the user component explicitly defined:

:account    {"name"=>"In", "user"=>{"email"=>"user@example.com", "password"=>"pass", "password_confirmation"=>"pass"}}

It has the additional side effect of removing the @account.users.build from within the else path as @numbers1311407 suggested

I am not certain whether their are other implications of this solution, I will need to work through it in the next few days, but for now I get the information I want defaulted into the view in the case of a failed create action.

@Beerlington and @numbers1311407 I appreciate the help in guiding me to the solution.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜