form updating nested attributes when not required - ruby-on-rails

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".

Related

unknown attribute 'development_id' for Lot with cocoon triple nested forms

I'm almost certain I know what the problem is, and if I'm correct, I just can't find where the problem is lying.
When triple-nesting with the cocoon gem, I'm having what seems to be an error with incorrect pluralisation. I have 3 resources, Developments > Lots > Listings, where Developments is the grandparent, Lots is the parent and Listings is the child.
The error I'm getting is unknown attribute 'development_id' for Lot. stemming from ActiveModel::UnknownAttributeError in Developments#new. I've checked my models and partials and have been playing around with them. Here is my code:
developments_controller.rb
class DevelopmentsController < ApplicationController
before_action :set_development, only: %i[ show edit update destroy ]
# GET /developments or /developments.json
def index
#developments = Development.all
end
# GET /developments/1 or /developments/1.json
def show
end
# GET /developments/new
def new
#development = Development.new
end
# GET /developments/1/edit
def edit
end
# POST /developments or /developments.json
def create
#development = Development.new(development_params)
respond_to do |format|
if #development.save
format.html { redirect_to #development, notice: "Development was successfully created." }
format.json { render :show, status: :created, location: #development }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #development.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /developments/1 or /developments/1.json
def update
respond_to do |format|
if #development.update(development_params)
format.html { redirect_to #development, notice: "Development was successfully updated." }
format.json { render :show, status: :ok, location: #development }
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: #development.errors, status: :unprocessable_entity }
end
end
end
# DELETE /developments/1 or /developments/1.json
def destroy
#development.destroy
respond_to do |format|
format.html { redirect_to developments_url, notice: "Development was successfully destroyed." }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_development
#development = Development.find(params[:id])
end
# Only allow a list of trusted parameters through.
def development_params
params.require(:development).permit(:development_name, :development_type, :development_address, :description, :estimated_completion_date, :body_corp,
lots_attributes: [:id, :status, :stage, :land_size, :price, :eta, :_destroy,
listings_attributes: [:id, :lot_number, :price, :type, :bed, :bath, :car, :house_size, :rent, :_destroy]])
end
end
development.rb (model)
class Development < ApplicationRecord
has_many :lots
accepts_nested_attributes_for :lots, reject_if: :all_blank, allow_destroy: :true
end
Lot.rb (model)
class Lot < ApplicationRecord
has_many :listings
accepts_nested_attributes_for :listings, reject_if: :all_blank, allow_destroy: :true
belongs_to :development
end
Listing.rb (model)
class Listing < ApplicationRecord
belongs_to :lot
end
_form.html.erb (partial)
<%= form_for #development do |f| %>
<div class="field">
<%= f.label :development_name%>
<%= f.text_field :development_name%>
</div>
<div class="field">
<%= f.label :development_type %>
<%= f.text_field :development_type %>
</div>
<div class="field">
<%= f.label :development_address %>
<%= f.text_field :development_address %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :estimated_completion_date %>
<%= f.date_select :estimated_completion_date %>
</div>
<div class="field">
<%= f.label :body_corp %>
<%= f.number_field :body_corp %>
</div>
<div id="listings">
<%= f.fields_for :lots do |lot| %>
<%= render 'lot_fields', f: lot %>
<% end %>
<div class="links">
<%= link_to_add_association 'add lot', f, :lots %>
</div>
</div>
<%= f.submit %>
<% end %>
_lot_fields.html.erb (partial)
<div class="nested-fields">
<h3> New Lots </h3>
<div class="field">
<%= f.label :status %>
<br/>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :stage %>
<br/>
<%= f.text_field :stage %>
</div>
<div class="field">
<%= f.label :land_size %>
<br/>
<%= f.text_field :land_size %>
</div>
<div class="field">
<%= f.label :price %>
<br/>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :eta %>
<br/>
<%= f.text_field :eta %>
</div>
<%= link_to_remove_association "remove lot", f %>
</div>
_listing_fields.html.erb (partial)
<div class="nested-fields">
<h3> New Listing </h3>
<div class="field">
<%= f.label :status %>
<br/>
<%= f.text_field :status %>
</div>
<div class="field">
<%= f.label :stage %>
<br/>
<%= f.text_field :stage %>
</div>
<div class="field">
<%= f.label :land_size %>
<br/>
<%= f.text_field :land_size %>
</div>
<div class="field">
<%= f.label :price %>
<br/>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :eta %>
<br/>
<%= f.text_field :eta %>
</div>
<%= link_to_remove_association "remove listing", f %>
</div>
new.html.erb
<h1>New Development</h1>
<%= render 'form', development: #development %>
<%= link_to 'Back', developments_path %>
I have a clone project of this and I noticed in the schema.rb that there is a key difference:
t.index ["developments_id"], name: "index_lots_on_developments_id"
I believe it should be development_id instead of being pluralised, but I'm not sure where/how this needs to be changed. I'm under the impression you should never alter a schema file.
Any help is greatly appreciated!
Exactly, it should be :
t.index ["development_id"], name: "index_lots_on_development_id"
You have to rewrite a migration with
def change
remove_index :lots, name: "index_lots_on_developments_id"
add_reference :lots, :development, index: true
end

Keep getting "Param is missing or the value is empty"

I keep getting the error mentioned above, even though my form object matches my controller object (most people who had this problem had mixed their objects up in similar questions). What exactly am I doing wrong here?
My code is below:
<!-- The form is here -->
<%= form_for(#user), url: {action: "/users/signup"}, html: {class: "signup_form"} do |f| %>
<%= label_tag(:firstname, "Firstname: ") %>
<%= f.text_field :firstname %>
<%= label_tag(:lastname, "Lastname: ") %>
<%= f.text_field :lastname %>
<%= label_tag(:username, "Username: ") %>
<%= f.text_field :username %>
<%= label_tag(:email, "Email: ") %>
<%= f.text_field :email %>
<%= label_tag(:password, "Password") %>
<%= f.password_field :password %>
<%= f.submit "Create Account" %>
<% end %>
And here's the controller:
class UsersController < ApplicationController
def create
#user = User.new(user_params)
#user.save
redirect_to #user
end
private
def user_params
params.require(:user).permit(:firstname, :lastname, :username,
:email, :password)
end
end
Would really appreciate some help and sorry if I missed something silly here, thanks in advance.
users_controller.rb
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, success: 'Thanks for signing up!' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
users/_form.html.erb
<%= form_for(#user) do |f| %>
<div class="field">
<%= f.label :first_name, "First name" %><br>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name, "Last name" %><br>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :email, "Email" %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password, "Password" %><br>
<%= f.password_field :password %>
</div>
<div class="actions">
<%= f.submit "Sign Up" %>
</div>
<% end %>
Why do you have url: {action: "/users/signup"} in the form? It is much simpler if you do the following:
routes.rb
Rails.application.routes.draw do
resources :user
end
views/users/_form.html.erb
<%= form_for(#user), html: {class: "signup_form"} do |f| %>
controllers/users_controller.rb (add the following)
class UsersController < ApplicationController
def new
#user = User.new
end
end
If you do it like this, form_for will automatically detect whether or not the #user object has been saved in the database, and will route the form to the create or update method respectively.
I general I don't reccommend altering the standard RESTful routes unless absolutley necessary. A user is not likely going to look at yoursite.com/users/new in the URL and be upset or confused.

Rails 4: can't update current user

I'm trying to update some specific values for current_user but I have had no luck with it. The values do not save and the page doesn't get redirected anywhere whatsoever. Here's the code for user controller in update
def update
#user = current_user
respond_to do |format|
if #user.update_attributes(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit, notice: 'Update unsuccessful' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def user_params
params.require(:user).permit(:username, :password, :password_confirmation, :email, :weight, :height, :age, :activity_level, :goal, :calorie_goal, :fat_goal, :carb_goal, :protein_goal, :fiber_goal, :sugar_goal)
end
I tried to use params[:user] or params[:current_user] instead of user_params but with no luck.. here's html
<h2>Set Up Your Profile</h2>
<% #user = current_user %>
<%= form_for(#user) do |f| %>
current user is <%= current_user.username %>
<div class="field">
<%= f.label :weight %><br>
<%= f.text_field :weight %>
</div>
<div class="field">
<%= f.label :height %><br>
<%= f.text_field :height %>
</div>
<div class="field">
<%= f.label :age %><br>
<%= f.number_field :age %>
</div>
<div class="field">
<%= f.label :activity_level %><br>
<%= f.number_field :activity_level %>
</div>
<div class="field">
<%= f.label :goal %><br>
<%= f.number_field :goal %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
here I tried form_for(current_user) as well but didn't work, too... Any ideas what's wrong here?

How do I create a two-step form in rails?

I have a question for rails. I'm creating a form for user to register. What I want to do is that after the user press "Submit" button I want to redirect the user to another page which shows all the information from the form filled by the user just now (read-only).
This is my controller
class PermitsController < ApplicationController
before_action :set_permit, only: [:show, :destroy]
def index
#permits = Permit.all
end
def new
#permits = Permit.new
end
def create
#permits = current_user.permits.build(permit_params)
if #permits.save
redirect_to invoice_path
else
render 'new'
end
end
def destroy
Permit.destroy_all(user_id: current_user)
respond_to do |format|
format.html { redirect_to root_path, notice: 'Permit was successfully canceled.' }
format.json { head :no_content }
end
end
def invoice
#permits = current_user.permits(permit_params)
end
def show
#user = User.find(params[:id])
#permits = #user.permits.paginate(permit_params)
end
def update
#permits = Permit.where(user_id: current_user).take
respond_to do |format|
if #permits.update(permit_params)
format.html { redirect_to root_path}
flash[:success] = "Permit successfully updated"
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_permit
#permits = Permit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def permit_params
params.require(:permit).permit(:vehicle_type, :name, :studentid, :department, :carplate, :duration, :permitstart, :permitend)
end
end
This is the form filled by user
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits) do |f| %>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, class: 'form-control' %>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, class: 'form-control' %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, class: 'form-control' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, class: 'form-control' %>
<%= f.label :permit_start %>
<%= f.date_select :permitstart, class: 'form-control' %>
<%= f.label :permit_end %>
<%= f.date_select :permitend, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
You could add an additional action between new and create.
# config/routes.rb
resources :permit do
collection do
post :confirm
end
end
The reason that we use POST even if the route does not create a resource is that we don't want to pass any user info in the request URL.
class PermitsController < ApplicationController
# POST /permits/confirm
def confirm
#fields = %i[vehicle_type, carplate, studentid, name, department, permitstart, permitend]
#permit = current_user.permits.build(permit_params)
render :new and return unless #permit.valid?
end
end
render :new and return unless #permit.valid? shortcuts the process and renders the :new form again if the input is not valid in the first place.
Since we are using POST we need a form for both the new.html.erb and confirm.html.erb all duplicating all those inputs would not be great so lets extract them to a partial:
<% # /views/permits/_inputs.html.erb %>
<%
input_options ||= {}
input_options[:class] ||= 'form-control'
%>
<%= f.label :"Vehicle" %>
<%= f.text_field :vehicle_type, input_options%>
<%= f.label :"License Plate" %>
<%= f.text_field :carplate, input_options %>
<%= f.label :"Student ID" %>
<%= f.text_field :studentid, input_options %>
<%= f.label :name %>
<%= f.text_field :name, input_options %>
<%= f.label :"Department of applicant" %>
<%= f.text_field :department, input_options %>
<%= f.label :permit_start %>
<%= f.date_select :permitstart, input_options %>
<%= f.label :permit_end %>
<%= f.date_select :permitend, input_options %>
So lets point the new.html.erb form so that it submits to /permits/confirm:
<% provide(:title, 'New Permit') %>
<h1>Permit Application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits, url: '/permits/confirm_permits_path') do |f| %>
<% render partial: :inputs %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
And create a /views/permits/confirm.html.erb view:
<% provide(:title, 'Confirm Permit application') %>
<h1>Confirm Permit application</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#permits) do |f| %>
<% render partial: :inputs, input_options: { readonly: 'readonly' } %>
<% end %>
</div>
</div>

Linking two models in a multi-model form

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.

Resources