Linking two models in a multi-model form - ruby-on-rails

I have a nested multimodel form right now, using Users and Profiles.
Users has_one profile, and Profile belongs_to Users.
When the form is submitted, a new user is created, and a new profile is created, but they are not linked (this is the first obvious issue). The user's model has a profile_id row, and the profile's model has a user_id row.
Here is the code for the form:
<%= form_for(#user, :url => teams_path) do |f| %>
<p><%= f.label :email %><br />
<%= f.text_field :email %></p>
<p><%= f.label :password %><br />
<%= f.password_field :password %></p>
<p><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></p>
<%= f.hidden_field :role_id, :value => #role.id %></p>
<%= f.hidden_field :company_id, :value => current_user.company_id %></p>
<%= fields_for #user.profile do |profile_fields| %>
<div class="field">
<%= profile_fields.label :first_name %><br />
<%= profile_fields.text_field :first_name %>
</div>
<div class="field">
<%= profile_fields.label :last_name %><br />
<%= profile_fields.text_field :last_name %>
</div>
<% end %>
<p><%= f.submit "Sign up" %></p>
<% end %>
A second issue, is even though the username, and password are successfully created through the form for the user model, the hidden fields (role_id & company_id - which are also links to other models) are not created (even though they are part of the model) - the values are successfully shown in the HTML for those fields however.
Any help would be great!
As requested, the controller code:
def new
#user = User.new
#user.profile = Profile.new
#role = Role.find_by_name("Regular")
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #teams }
end
end
def create
#user = User.new(params[:user])
#profile = Profile.new(params[:profile])
respond_to do |format|
if #profile.save && #user.save
format.html { redirect_to (teams_path) }
format.xml { render :xml => #profile, :status => :created, :location => #profile }
else
format.html { render :action => "new" }
format.xml { render :xml => #profile.errors, :status => :unprocessable_entity }
end
end
end

To answer question number one, change the following:
#profile = Profile.new(params[:profile])
to
#profile = #user.profile.build(params[:profile]) #In the case of a has_many relationship
or
#profile = #user.build_profile(params[:profile]) #In the case of a has_one relationship
The build command builds a new profile with the user_id properly set.
For the second question, can you delete the query for Role and Company during the new action and instead assign those during the create action? This would remove the necessity of passing hidden parameters.

Related

Nested model, update_attributes not working

I am having a hard time with what I thought would be an textbook update example. Searched SO but couldn't find the answer. In short, when I click submit the user_id in the profile model gets wiped out and no other data gets saved. I am using Rails 3.2.2.
Here is what I have...
User model...
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_one :profile
accepts_nested_attributes_for :profile
end
Profile model...
class Profile < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
belongs_to :user
attr_accessible :first_name, :last_name
end
Users controller...
class UsersController < ApplicationController
def new
#user = User.new
#user.accounts_users.build()
#user.build_profile()
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
Nested form...
<%= form_for #user, :validate => true do |f| %>
<%= f.fields_for :profile do |p| %>
<fieldset>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Edit Profile', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
<% end %>
Edit: I edited the UsersController to include the new action. Why would the new action affect the edit/update actions?
I had a similar problem before. Can we see the code for your new action? Do you have #user.build_profile in there (right beneath #user = User.new)? Or is the new action working fine?

form updating nested attributes when not required

I have a Users model that can have many Groups through Memberships. If a user is a member of a group, and you submit the form with no changes, it will attempt to update the membership table and put group_id to 0 as per below.
My model for User accepts_nested_attributes_for Memberships
In my User controller...
def edit
#user = User.find(params[:id])
#groups = Group.current
#membership = #user.memberships.build
end
def update
#user = User.find(params[:id])
#groups = Group.current
if params[:memberships][:group_id] != ""
#membership = #user.memberships.build(:group_id => params[:memberships][:group_id])
end
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to admin_users_url, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
#if params[:memberships][:group_id] == ""
# #membership = #user.memberships.build
#end
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
In my user form...
<%= form_for([:admin,#user]) do |f| %>
---
<div class="field">
<%= f.label :email %><br />
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<%= f.fields_for :memberships do |builder| %>
<div class="field">
<% if builder.object.new_record? %>
<%= builder.label :group_id %><br />
<%= collection_select(:memberships, :group_id, #groups, 'id', 'name', {:include_blank => true}) %>
<% else %>
<%= builder.label :group_id %><br />
<%= builder.text_field :group_id, :value => Group.find(Membership.find(builder.object).group_id).name, :readonly => true %> <%= link_to 'Remove', [:admin,#user,Membership.find(builder.object)], :confirm => 'Are you sure?', :method => :delete %>
<% end %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The form is submitting the data
Parameters: {"utf8"=>"✓",
"authenticity_token"=>"6Vp9sgMySzRm2CU9Dko+Jpf6yaBkXjKkt10UDbb8dcw=",
"user"=>{"email"=>"asdfasdf#asdf.com",
"password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]",
"memberships_attributes"=>{"0"=>{"group_id"=>"Cricket Maidstone June 2012",
"id"=>"18"}}},
"memberships"=>{"group_id"=>""},
"commit"=>"Update User", "id"=>"25"}
All I want to do is for the form to check and if there is nothing in the collection_select field to not update the memberships table, but still update any changes to email/password. Can anyone see a way to do this?
In your model, declare a parallel field with attr_accessor:
attr_accessor :memberships_attributes_input
def memberships_attributes_input
memberships
end
def memberships_attributes_input=value
# Update memeberships if required evaluating 'value'.
end
Use this new field in your form instead "memberships".

NoMethodError in ProfilesController#create

I'm trying to create an associated form for Profiles but for some reason when I hit the submit button, I'm getting a NoMethodError which doesn't make sense to me as my code is exactly the same as a tutorial I'm following... unless the tutorial is outdated..
def create
#user = User.find(params[:user_id])
#profile = #user.profiles.create(params[:profile])
redirect_to user_path(#user)
end
Anyone know why I'm getting the noname errors?
The form that belongs to the controller is below:
https://github.com/imjp/SuperModel/blob/master/app/views/users/show.html.erb
EDIT 1: Fixed! The following code isn't displaying the profile data at http://localhost:3000/users/2 (which is profile#show) though: <%= #user.profile.first_name %>
Here's my current profiles#show
def show
#user = User.find(params[:user_id])
#profile = #user.profile.find(params[:id])
end
EDIT 2: I've updated my github repository at https://github.com/imjp/SuperModel
why #user.profiles? try #user.profile (singular)
You're getting undefined method 'first_name' for nil:NilClass because a User with that id could not be found. You're probably sending a wrong param or something. How does a URL for your show action look like?
edit:
change your create method in users_controller.rb to this
def create
#user = User.new(params[:user])
#user.build_profile
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
After you've changed this, change your form in app/views/users/show.html.erb to this
<h3>Add Profile</h3>
<%= form_for([#user, #user.profile]) do |f| %>
<div class="field">
<%= f.label :first_name %><br />
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %><br />
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :picture %><br />
<%= f.text_field :picture %>
</div>
<div class="field">
<%= f.radio_button(:sex, "male") %>
<%= f.label(:sex, "Male") %>
<%= f.radio_button(:sex, "female") %>
<%= f.label(:sex, "Female") %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
and it will work. Your user-profile association was not built.

Setting Symbols in Form View - Ruby on Rails

I am having problems trying to set symbol values in a form view. My view has a couple instance variables being used, #task and #team, #team is the one im having issues with. Tasks have a :team value that needs to be set. In this view #team holds a value, but when I hit the "Create" button and make a post, the #team value is lost, and #task contains no team value.
Here is the view I'm dealing with:
Note: the ":team => #task.team" line doesn't work
<% form_for(#task) do |f| %>
<%= f.error_messages %>
<% #task.team = Team.find(#team) %>
<p><%= #task.team.title%></p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= f.label :hours %><br />
<%= f.text_field :hours %>
</p>
<p>
<%= f.label :team %><br />
<% :team => #task.team %>
</p>
<p>
<%= f.submit 'Create'%>
</p>
<% end %>
The Post method that gets called on Create:
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to(#task, :notice => 'Task was successfully created.') }
format.xml { render :xml => #task, :status => :created, :location => #task }
else
format.html { render :action => "new" }
format.xml { render :xml => #task.errors, :status => :unprocessable_entity }
end
end
end
#Jordinl is right when he mentions that you could use a hidden form field. You can also automatically scope the task to the team in the controller by doing something like
#team = Team.find(params[:team])
and then
#team.tasks << Task.new(params[:task])
You'll need to have a has_many association set up in the team model for tasks
has_many :tasks
for this to work. Each task will also need the team id as well but it sounds like you already have that.
Why don't you set it as a hidden field?
<%= f.hidden_field :team, :value => #task.team %>

How to use a nested form for multiple models in one form?

I'm struggling to come up with the proper way to design a form that will allow me to input data for two different models. The form is for an 'Incident', which has the following relationships:
belongs_to :customer
belongs_to :user
has_one :incident_status
has_many :incident_notes
accepts_nested_attributes_for :incident_notes, :allow_destroy => false
So an incident is assigned to a 'Customer' and a 'User', and the user is able to add 'Notes' to the incident. I'm having trouble with the notes part of the form. Here how the form is being submitted:
{"commit"=>"Create",
"authenticity_token"=>"ECH5Ziv7JAuzs53kt5m/njT9w39UJhfJEs2x0Ms2NA0=",
"customer_id"=>"4",
"incident"=>{"title"=>"Something bad",
"incident_status_id"=>"2",
"user_id"=>"2",
"other_id"=>"AAA01-042310-001",
"incident_note"=>{"note"=>"This is a note"}}}
It appears to be attempting to add the incident_note as a field under 'Incident', rather than creating a new entry in the incident_note table with an incident_id foreign key linking back to the incident.
Here is the 'IncidentNote' model:
belongs_to :incident
belongs_to :user
Here is the form for 'Incident':
<% form_for([#customer,#incident]) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :other_id, "ID" %><br />
<%= f.text_field :capc_id %>
</p>
<p>
<%= f.label :title %><br />
<%= f.text_field :title %>
</p>
<p>
<%= label_tag 'user', 'Assign to user?' %>
<%= f.select :user_id, #users.collect {|u| [u.name, u.id]} %>
</p>
<p>
<%= f.label :incident_status, 'Status?' %>
<%= f.select :incident_status_id, #statuses.collect {|s| [s.name, s.id]} %>
</p>
<p>
<% f.fields_for :incident_note do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
</p>
<p>
<%= f.submit "Create" %>
</p>
<% end %>
And finally, here are the incident_controller entries for New and Create.
New:
def new
#customer = current_user.customer
#incident = Incident.new
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident_note = IncidentNote.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #incident }
end
end
Create:
def create
#users = #customer.users
#statuses = IncidentStatus.find(:all)
#incident = Incident.new(params[:incident])
#incident.customer = #customer
#incident_note = #incident.incident_note.build(params[:incident_note])
#incident_note.user = current_user
respond_to do |format|
if #incident.save
flash[:notice] = 'Incident was successfully created.'
format.html { redirect_to(#incident) }
format.xml { render :xml => #incident, :status => :created, :location => #incident }
else
format.html { render :action => "new" }
format.xml { render :xml => #incident.errors, :status => :unprocessable_entity }
end
end
end
I'm not really sure where to look at this point. I'm sure it's just a limitation of my current Rails skill (I don't know much). So if anyone can point me in the right direction I would be very appreciative. Please let me know if more information is needed!
Thanks!
Check api for fields_for method and scroll to one-to-many section.
Your model has many :incident_notes, not one incident_note, that is why it doesn't understand relationship and tries to find a field with this name.
So it should be:
<% f.fields_for :incident_notes do |inote_form| %>
<%= inote_form.label :note, 'Add a Note' %>
<%= inote_form.text_area :note, :cols => 40, :rows => 20 %>
<% end %>
It iterates through all incident_notes assigned to incident and produces fields for each of them.
You also have to build at least one note in you new action, otherwise there will be none:
def new
#incident = Incident.new
#incident.incident_notes.build
# ...
end

Resources