My User model has_many Responses. I'm trying to create a nested form to create a new user with three child responses. My code looks identical to the Rails cast, but although it will save the user it does not save their responses. Can anyone see what is wrong?
users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
3.times{@user.responses.build}
@responses = @user.responses
end
def create
@user = User.new(user_params)
if @user.save
redirect_to @user #todo: where do we want to redirect?
else
render 'new'
end
end
def show
@user = User.find(params[:id])
@responses = @user.responses
end
def index
@users = User.all
end
private
def user_params
params.require(:user).permit(:username, :email, :responses)
end
end
user.rb
class User < ActiveRecord::Base
attr_accessor :responses_attributes
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates :responses, presence: true
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id, :content
end
users/new.html.erb (the form)
<h1>This is a test form for adding Users with child Responses</h1>
<%= form_for @user do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :username %>
<%= f.text_field :username %>
<%= f.label :email %>
<%= f.text_field :email %>
<p>
<%= f.fields_for :responses do |builder| %>
<%= builder.label :content, "Response" %>
<%= builder.text_field :content %>
<% end %>
</p>
<%= f.submit "Submit" %>
<% end %>
Edit:
I have change the strong parameters in the Users controller to:
def user_params
params.require(:user).permit(:username, :email, responses_attributes: [:content])
end
And I have updated the User model to:
class User < ActiveRecord::Base
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
before_save { self.email = email.downcase }
validates :username, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX } ,
uniqueness: {case_sensitive: false};
validates_associated :responses, presence: true
end
Now it continues to fail my validations for the responses, and also fails my validations for username and email.
The problem is in your Response class:
validates_presence_of :user_id, :content
This validation requires the existence of user_id
, which means the User has to be created before Response. To make it work you can try couple of things:
Change validates_presence_of :user_id
to validates_presence_of :user
to make it validate the User
object instead of the user's id.
Use inverse_of
option. See the Rails API doc.
Updated code.
user.rb
class User < ActiveRecord::Base
attr_accessible :responses_attributes
has_many :responses, :dependent => :destroy, :inverse_of => :user
accepts_nested_attributes_for :responses
# ...
end
response.rb
class Response < ActiveRecord::Base
belongs_to :user, :inverse_of => :responses
validates_presence_of :user, :content
end
Answer for RoR v3:
The problem is the following line in your User
model:
attr_accessor :responses_attributes
Replace it with:
attr_accessible :responses_attributes
The answer to question Difference between attr_accessor and attr_accessible
should help you understand the difference between the two.
Also your validation validates :responses, presence: true
needs to be updated to use validates_associated
:
validates_associated :responses, presence: true
Answer for RoR v4:
I hadn't noticed the use of Strong parameters ealier, apologies for this. A couple of changes should fix your issue:
attr_accessor :responses_attributes
from User
model.validates :responses, presence: true
with validates_associated :responses, presence: true
responses
attributes in permit list.Something like follows:
class User < ActiveRecord::Base
# attr_accessor :responses_attributes # Remove this line
has_many :responses, :dependent => :destroy
accepts_nested_attributes_for :responses#, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
...
validates_associated :responses, presence: true # Update here
end
#Users Controller
class UsersController < ApplicationController
...
private
def user_params
params.require(:user).permit(:username, :email, :responses_attributes[:content])
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With