Rails - Nested Form - ruby-on-rails

I have Users who bet on matches. A single bet is called "Tipp" and the users predict the match score in "tipp.tipp1" and "tipp.tipp2"
I have problems with my form which is supposed to save "tipps" of users.
With the code below I get "Can't mass-assign protected attributes: tipp" although i have set "accepts_nested_attributes_for :tipps" and "attr_accessible :tipps_attributes".
I hope I have provided all the necessary code. Thanks in advance for your help!
Here is the parameters output:
Parameters:
{
"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"mPPpCHjA3f/M2l1Bd3ffO1QUr+kdETGkNE/0CNhbJXE=",
"user" =>{
"tipp"=>{
"6"=>{"tipp1"=>"4","tipp2"=>"6"},
"7"=>{"tipp1"=>"-1","tipp2"=>"-1"},
"8"=>{"tipp1"=>"-1","tipp2"=>"-1"}
}
},
"commit"=>"Update User",
"user_id"=>"1"
}
Shortened Code:
Controllers:
1) Users
class UsersController < ApplicationController
def edit_tipps
#user = current_user
end
def update_tipps
#user = current_user
if #user.update_attributes(params[:user])
flash[:notice] = "success (maybe)"
redirect_to user_edit_tipps_path(#user)
else
flash[:error] = "errors"
redirect_to user_edit_tipps_path(#user)
end
end
Models:
1) Users
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes
has_many :tipps
accepts_nested_attributes_for :tipps
end
2) Tipps
class Tipp < ActiveRecord::Base
attr_accessible :match_id, :points, :round_id, :tipp1, :tipp2, :user_id
belongs_to :user
end
My Form:
<%= form_for #user, :url => { :action => "update_tipps" } do |user_form| %>
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
<%= tipp_form.text_field :tipp1 %><br/>
<%= tipp_form.text_field :tipp2 %><br/>
<% end %>
<% end %>
<%= submit_or_cancel(user_form) %>
<% end %>

Instead of doing what you did,
you could try either:
1.
Instead of:
<% #user.tipps.each do |tipp| %>
<%= user_form.fields_for tipp, :index => tipp.id do |tipp_form|%>
I would do this:
<%= user_form.fields_for :tipps do |tipp_form| %>
Or:
2.
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :tipps_attributes, :tipps
Goodluck

Related

undefined method `class_detail' for nil:NilClass

I have two tables class_details and user_classes. user_classes table is dependent of user table and class_details and class_details is independent of user table. Now my requirement is that when i click a button few details must be saved to the database which belong to different tables. User dependent table are only getting saved to database and not the user independent tables and i am getting error undefined method class_detail for nil:NilClass
Controller code
def update_profile
if #user.update(user_params)
redirect_to profile_index_path, notice: 'Profile was successfully updated.'
else
render :index
end
end
end
private
def set_user
#user = User.find(current_user.id)
#user.fee || #user.build_fee
#user.user_class || #user.build_user_class
end
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
end
class_detail.rb
class ClassDetail < ApplicationRecord
has_one :user_class, dependent: :destroy
end
user_class.rb
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
validates_presence_of :user_id
end
user.rb
has_one :fee, dependent: :destroy
accepts_nested_attributes_for :fee
has_one :user_class, dependent: :destroy
view code
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= f.fields_for :class_detail, #user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
Can anyone help me updating user independent table! Thank you in advance
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: %i[id class_detail [id class_name class_category]])
That means you want to update class_detail from user_class, but you need to define nested attributes:
class UserClass < ApplicationRecord
belongs_to :user
belongs_to :class_details
accepts_nested_attributes_for :class_details, update_only: true
validates_presence_of :user_id
end
Also, the form must look like this:
<%= form_for(#user, url: {action: 'update_profile'}, html: {class: 'm-form m-form--fit m-form--label-align-right'}) do |f| %>
<%= f.fields_for :fee, #user.fee do |u| %>
<%= u.number_field :fee_hour, class: 'form-control m-input', placeholder: t('.fee_for_hour') %>
<% end %>
<%= f.fields_for :user_class, #user.user_class do |k| %>
<%= k.fields_for :class_detail, #user.user_class.class_detail do |c| %>
<%= c.text_field :class_name, class: 'form-control m-input' %>
<% end %>
<% end %>
<% end %>
And in your controller:
def user_params
params.require(:user).permit(:name, fee_attributes: %i[id fee_hour fee_month], user_class_attributes: [:id, { class_detail: %i[class_name class_category] }])
end
Edited:
That all means class_details and user_class are associated to the user already. Build references model - child-child-model - child-parent-model from the single call not possible. You can build this references in the edit action. For example:
def edit
#user.create_user_class!(class_detail: ClassDetail.find(n)) unless #user.user_class
end

Simple_form: simple_fields_for not showing the form

I'm trying to create two model in just one form. I have user and unit model both are associated with has_many :through association.
user.rb
class User < ActiveRecord::Base
has_many :units, :through => :user_unit_assocs
has_many :user_unit_assocs
end
unit.rb
class Unit < ActiveRecord::Base
has_many :users, :through => :user_unit_assocs
has_many :user_unit_assocs
end
users_controller.rb
class UsersController < ApplicationController
def create
#user = User.new(user_params)
#user.units.build
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
else
format.html { render :new }
end
end
end
private
def user_params
params.require(:user).permit(:username, :email, units_attributes:[:unitname])
end
end
This is my form, users new
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for :units do |unit| %>
<%= unit.input :unitname, required: true %>
<% end %>
<%= f.input :name, required: true %>
<%= f.input :email, required: true %>
<%= f.button :submit %>
<% end %>
When I run the code, the form only showing a input-field for :name and :email and there is no input-field for units:[:unitname]. How can I show the input-field in the form? Thanks in advance.
References:
1. Rails 4: accepts_nested_attributes_for and mass assignment
2. https://github.com/plataformatec/simple_form/wiki/Nested-Models
Add
def new
#user = User.new
#user.units.build
end
on your controller
You need to build the association in the new action, not the create action.

Update child object in nested form in Rails 4

My current devise config has a user object that has a single table inheritance structure that breaks down into two further user types (one of them is business). The child object of business I am trying to update is called 'supp_forms'. When I try and update the record I get the following error in terminal. I am using the nested_form_for gem to handle my nested forms.
Unpermitted parameters: supp_form_attributes
However, the parameters being passed through look correct (the data being passed through is the data that I have edited in the form).
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XX", "business"=>{"supp_form_attributes"=>{"work_phone_number"=>"(906) 790-6969 x69696", "business_address"=>"1 XXXX st", "business_postal_code"=>"L0R 1K2", "business_city"=>"Oria", "business_province"=>"ON", "employee_count"=>"5", "id"=>"96"}}, "commit"=>"Update Business"}
My update form looks like the following.
business_profile.html.erb
<%= nested_form_for #user, url: business_registration_path do |f| %>
<%= f.fields_for :supp_form do |supp_form| %>
<%= supp_form.label :work_phone_number %>
<%= supp_form.text_field :work_phone_number %>
<%= supp_form.label :business_address %>
<%= supp_form.text_field :business_address %>
<%= supp_form.label :business_postal_code %>
<%= supp_form.text_field :business_postal_code %>
<%= supp_form.label :business_city %>
<%= supp_form.text_field :business_city %>
<%= supp_form.label :business_province %>
<%= supp_form.text_field :business_province %>
<%= supp_form.label :employee_count %>
<%= supp_form.text_field :employee_count %>
<% end %>
<%= f.submit %>
<% end %>
business.rb
class Business < User
# Associations
has_one :supp_form
has_many :loan_applications
has_many :transactions
has_many :listing_information_forms
# Nested attributes
accepts_nested_attributes_for :supp_form
end
supp_form.rb
class SuppForm < ActiveRecord::Base
# Associations
belongs_to :business
end
supp_forms_controller.rb
class SuppFormsController < ApplicationController
before_filter :authenticate_user!
def edit
#user = User.current_user
end
def update
#user = current_user
#suppform = #user.supp_form
if #suppform.update_attributes(supp_form_params)
business_supp_form_path(#user)
else
render 'edit'
end
end
private
def supp_form_params
params.require(:supp_form).permit(:business_id, :title, :loan_agreement_authorization, :first_name, :last_name, :applicant_role, :work_phone_number, :business_address, :business_postal_code,:business_city, :business_name, :years_in_business, :legal_structure, :ownership, :business_industry, :employee_count, :mobile_phone_number, :business_province, :business_country)
end
end
business_account_controller.rb
class BusinessAccountController < ApplicationController
before_filter :authenticate_user!
def business_profile
#user = current_user
end
end
registrations_controller.rb (for businesses)
class Businesses::RegistrationsController < Devise::RegistrationsController
before_filter :update_sanitized_params
def edit
#user = current_user
super
end
def update
#user = current_user
super
end
private
def update_sanitized_params
devise_parameter_sanitizer.for(:sign_up) {|u| u.permit(:email, :password, :password_confirmation, :type, :confirmed_at, :business_name, :terms, :railsid, :terms_of_service, supp_form_attributes: [:business_id, :title, :loan_agreement_authorization, :first_name, :last_name, :work_phone_number, :applicant_role, :business_address, :business_postal_code, :business_city, :business_name, :years_in_business, :legal_structure, :ownership, :business_industry, :employee_count, :mobile_phone_number, :business_province, :business_country])}
end
end
Please try to do as following, it works on my side and hope it helpful for you.
class RegistrationsController < Devise::RegistrationsController
def create
devise_parameter_sanitizer.for(:sign_up) << { profile_attributes: [:first_name, :last_name] }
super
end
end

saving two Rails models with accepts_nested_attributes_for

I have two models: author and profile. Author accepts_nested_attributes_for profile. I can't figure out how to save the profile model (namely the :avatar attribute) through author.
author.rb
class Author < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
profile.rb
class Profile < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
belongs_to :author
end
authors_controller.rb
class AuthorsController < ApplicationController
before_action :set_author, only: [:show, :edit, :update, :destroy]
def index
#authors = Author.all.order("last_name ASC, first_name ASC")
end
def new
#author = Author.new
end
def create
#author = Author.new(author_params)
if #author.save
AuthorMailer.activate(#author).deliver
flash[:notice] = "Please check your email to activate your account."
redirect_to root_path
else
render :new
end
end
def edit
#profile = #author.build_profile
end
def update
if #author.update(author_params)
flash[:notice] = "Profile has been updated."
redirect_to #author
else
flash[:alert] = "Profile has not been updated."
render :edit
end
end
private
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, :avatar)
end
def set_author
#author = Author.find(params[:id])
end
end
app/views/authors/edit.html.erb
<div class="center">
<h1>Edit Author</h1>
</div>
<div class="row">
<div class="col-xs-6 col-lg-4 center-block">
<%= simple_form_for(#author, :defaults => { :wrapper_html => {:class => 'form-group'}, :input_html => { :class => 'form-control' } }) do |f| %>
<%= f.input :first_name %>
<%= f.input :last_name %>
<%= f.fields_for [#author, #profile] do |p| %>
<%= p.file_field :avatar %>
<% end %>
<%= f.submit "Submit", class: "btn small btn-default" %>
<% end %>
</div>
</div>
This form doesn't save anything to my profile table.
EDIT1
updating the permit parameters didn't save anything to the profile table. But I did notice that adding the following to my authors_controller in the update action saves an incomplete record to the profile table (the avatar field is blank):
author_controller#update
if #author.update(author_params)
#profile = #author.build_profile()
#author.profile = #profile
flash[:notice] = "Profile has been updated."
redirect_to #author
else
flash[:alert] = "Profile has not been updated."
render :edit
end
I tried placing pry inside the update action and my params look like this:
{"utf8"=>"✓",
"_method"=>"patch",
"authenticity_token"=>"EROrMzOejPmMU/wzlnC5iaoTPu4pyBXelHAs3uiPA2U=",
"author"=>
{"first_name"=>"Mike",
"last_name"=>"Glaz",
"profile"=>
{"avatar"=>
#<ActionDispatch::Http::UploadedFile:0x007feb48127ab0
#content_type="image/jpeg",
#headers=
"Content-Disposition: form-data; name=\"author[profile][avatar]\"; filename=\"me_and_lekeziah.jpg\"\r\nContent-Type: image/jpeg\r\n",
#original_filename="me_and_lekeziah.jpg",
#tempfile=#<File:/tmp/RackMultipart20140504-15793-l4uu6l>>}},
"commit"=>"Submit",
"action"=>"update",
"controller"=>"authors",
"id"=>"3"}
so then I tried the following in my update action:
#profile = #author.build_profile(params[:author][:profile])
#author.profile = #profile
but then I get the following error:
ActiveModel::ForbiddenAttributesError in AuthorsController#update
when you use "fields_for" for a association model xxx, your params need to permit :xxx_attributes fields, which's value should be a array include it's attributes, so change your author_params method to something like this:
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, profile_attributes: [:avatar])
end
First, you should update your params permitted fields as:
def author_params
params.require(:author).permit(:first_name, :last_name, :email, :password, :password_confirmation, :profile_attributes[:id, :avatar])
end
Second, you are mixing Rails form_for helper and Simple_form in your view.
change
<%= f.fields_for [#author, #profile] do |p| %>
to:
<%= f.simple_fields_for [#author, #profile] do |p| %>

Rails validations not working with habtm

for now i've got followings:
model => User (name, email)
has_and_belongs_to_many :trips
model => Trip (dest1, dest2)
has_and_belongs_to_many :users
validates :dest1, :dest2, :presence => true
model => TripsUsers (user_id, trip_id) (id => false)
belongs_to :user
belongs_to :trip
As you see from the code, trip model has validation on dest1, and dest2, but it's not showing up an errors. Controller and view defined as follow:
trips_controller.rb
def new
#user = User.find(params[:user_id])
#trip = #user.trips.build
end
def create
#user = User.find(params[:user_id])
#trip = Trip.new(params[:trip])
if #trip.save
#trip.users << #user
redirect_to user_trips_path, notice: "Success"
else
render :new
end
end
_form.html.erb
<%= simple_form_for [#user, #trip] do |f| %>
<%= f.error_notification %>
<%= f.input :dest1 %>
<%= f.input :dest2 %>
<%= f.submit "Submit" %>
<% end %>
According to the rails guide on presence validation, it can't be used with associated objects. Try to use a custom validation:
validate :destinations_presence
def destinations_presence
if dest1.nil?
errors.add(:dest1, "missing dest1")
elsif dest2.nil?
errors.add(:dest1, "missing dest2")
end
end

Resources