I have a user which is a nested resource of account, and I'm trying to create the account and the user in 1 form. The account is saving correctly, however no user record is being written.
user.rb:
class User < ApplicationRecord
belongs_to :account
validates :email, presence: true
end
account.rb:
class Account < ApplicationRecord
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users
end
accounts_controller.rb:
class AccountsController < ApplicationController
def new
#account = Account.new
#user = #account.users.build
end
def create
#account = Account.new(account_params)
#account.secret = SecureRandom.uuid
if #account.save
flash[:success] = "You've successfully created your account, now it's time to create your first team."
redirect_to dashboard_path
else
flash[:danger] = "There was a problem creating your account"
redirect_to signup_path
end
end
private
def account_params
params.require(:account).permit(:name, user_attributes: [:first_name, :last_name, :email, :password, :password_confirmation, :secret])
end
end
accounts/new.html.erb:
<h1>Signup</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(#account) do |form| %>
<%= form.label "Account name" %>
<%= form.text_field :name, class: "form-control" %>
<%= form.fields_for :user do | f| %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: "form-control" %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: "form-control" %>
<%= f.label :email %>
<%= f.text_field :email, class: "form-control" %>
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
<% end %>
<%= form.submit "Signup", class: "btn btn-primary" %>
<% end %>
</div>
</div>
When I submit the the form and check my logs, I'm seeing:
Processing by AccountsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"p5+CRICRp3ZilIg1NtehEQG7Gh2amnFFUD5NawtqkICJ4uRDvvJxf2WTbd7+rnfG9zdblT1QWgfJ62NNxcc2RA==", "account"=>{"name"=>"Streame", "user"=>{"first_name"=>"Jeremy", "last_name"=>"Kirkham", "email"=>"jeremy#streame.com.au", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}}, "commit"=>"Signup"}
Unpermitted parameter: user
The answer was quite simple in the end - I needed to change the form from
<%= form.fields_for :user do | f| %>
to
<%= form.fields_for :users do | f| %>
(note user -> users), and the controller from
params.require(:account).permit(:name, user_attributes:
to
params.require(:account).permit(:name, users_attributes:
(again note user -> users).
Basically it came down to pluralisation!
Related
I have two account types with the relationship below laid out in models below. I need to build a registration form for #account that has a form select field where a user can select to register for either a student or partner account and for this account record to save according to the selection (i.e. to the students or partners table and the accounts table).
I'm running into issues on the controller Accounts#new method and I'm not sure how to set this up in a way that works.
Account:
class Account < ApplicationRecord
belongs_to :acct_holderable, :polymorphic => true
Student:
class Student < ApplicationRecord
has_one :account, :as => :acct_holderable
Partners:
class Partner < ApplicationRecord
has_one :account, :as => :acct_holderable
View for Accounts#new
<%= form_for(#account) do |f| %>
<%= render 'shared/error_messages', object: #account %>
<%= f.label :account_type %>
<%= f.select :acct_holderable, options_for_select(account_type, #account.account_holderable_type), class: 'form-control' %>
<%= f.label :first_name %>
<%= f.text_field :first_name, class: 'form-control' %>
<%= f.label :last_name %>
<%= f.text_field :last_name, class: 'form-control' %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
Account Helper (for options_select)
def account_type
input =<<-OPTIONS
Student,
Partner,
Other Account TBU,
Other Account TBU
input.split(',')
end
Accounts controller
def new
#account = Account.new
end
def create
#account = Account.new(account_params)
if #account.save
#account.send_activation_email
flash[:info] = "Please check your email to activate your account before logging in!"
redirect_to login_url
else
render 'new'
end
end
Pls check the following link for more info http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
https://6ftdan.com/allyourdev/2015/02/10/rails-polymorphic-models/
These links help you to get better understand about polymorphic association.
I have set up polymorphic resource using accepts_nested_attributes_for but for some reason whenever I try to update, instead of just updating the current record, it just create a new one instead. Can anyone tell me what the problem is? Thanks.
vendors_controller.rb
class VendorsController < ApplicationController
before_action :authenticate_user! #check if user logged in
before_action :set_vendor, only: [:show, :edit, :update, :destroy]
def index
#vendors = current_company.vendors
end
def new
#vendor = current_company.vendors.build
#vendor.addresses.build
end
def create
#vendor = current_company.vendors.build(vendor_params)
if #vendor.save
flash[:notice] = 'New Vendor Added'
redirect_to vendors_url
else
flash[:error] = 'Could not save vendor information. Please try again.'
render 'new'
end
end
def show
end
def edit
##vendor = Vendor.find(params[:id])
##vendor.addresses.find_or_initialize_by(addressable_id: #vendor.id, addressable_type: 'Vendor')
end
def update
##vendor = Vendor.find(params[:id])
if #vendor.update(vendor_params)
flash[:notice] = 'Vendor Updated'
redirect_to vendors_path
else
flash[:error] = 'Could not save vendor information. Please try again.'
render 'edit'
end
end
def destroy
#Vendor.find(params[:id]).destroy
#vendor.destroy
flash[:notice] = "Asset deleted"
redirect_to vendors_url
end
private
def vendor_params
params.require(:vendor).permit(:name, :vendorID, :contact, :email, :phone, :image, :remove_image, addresses_attributes: [:street_1, :street_2, :city, :state, :zip] )
end
def set_vendor
#vendor = Vendor.find(params[:id])
end
def current_company
current_user.company
end
end
_form.html.erb
<div class="row">
<div class="col-md-4 col-md-offset-4">
<div class="card card-block" style="background-color:#f5f5f5">
<%= form_for #vendor do |f| %>
<div class="form-group">
<%= f.label :name, 'Vendor Name' %><br />
<%= f.text_field :name, autofocus: true, required: true, class:'form-control' %>
</div>
<div class="form-group">
<%= f.label :vendorID, 'Vendor ID/Number' %><br />
<%= f.text_field :vendorID, class:'form-control' %>
</div>
<div class="form-group">
<%= f.label :contact, 'Main Contact Person' %><br />
<%= f.text_field :contact, class:'form-control' %>
</div>
<div class="form-group">
<%= f.label :email %><br />
<%= f.email_field :email, class:'form-control' %>
</div>
<div class="form-group">
<%= f.label :phone %><br />
<%= f.phone_field :phone, class:'form-control' %>
</div>
<%= f.fields_for :addresses do |a| %>
<div class="form-group">
<%= a.label :street_1, 'Street' %><br />
<%= a.text_field :street_1, class:'form-control' %>
</div>
<div class="form-group">
<%= a.label :street_2, 'Street 2 (Optional)' %><br />
<%= a.text_field :street_2, class:'form-control' %>
</div>
<div class="form-group">
<%= a.label :city, 'City' %><br />
<%= a.text_field :city, class:'form-control' %>
</div>
<div class="form-group">
<%= a.label :state, 'State' %><br />
<%= a.text_field :state, class:'form-control' %>
</div>
<div class="form-group">
<%= a.label :zip, 'Zip Code' %><br />
<%= a.text_field :zip, class:'form-control' %>
</div>
<% end %>
<div class="actions form-group"><br>
<%= f.submit 'Save Vendor', class: 'btn btn-primary' %>
<%= link_to 'Cancel', :back, class: 'btn btn-default' %>
</div>
<% end %>
</div>
vendor.rb
class Vendor < ActiveRecord::Base
belongs_to :company
has_many :asset
has_many :addresses, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :addresses, reject_if: RejectDeeplyNested.blank?
end
address.rb
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
end
you need to change your permitted parameters like this
params.require(:vendor).permit(:name, :vendorID, :contact, :email, :phone, :image, :remove_image, addresses_attributes: [:street_1, :street_2, :city, :state, :zip, :id] )
You need to add optional: true to your relationship:
class Address < ApplicationRecord
belongs_to :addressable, polymorphic: true, optional: true
end
Forgot to mention, that optional is available only on Rails > 5
Rails 4.2.8
user.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :rememberable,
:validatable, :encryptable, :omniauthable, omniauth_providers: [:facebook], encryptor: :restful_authentication_sha1
attr_accessible :email, :name, :password, :password_confirmation, :is_admin, :is_master
has_one :customer
accepts_nested_attributes_for :customer
end
customer.rb
class Customer < ActiveRecord::Base
belongs_to :user
accepts_nested_attributes_for :user
end
customers_contoller.rb
class CustomersController < ApplicationController
def edit
#customer = current_user.customer
end
def update
#customer = current_user.customer
#customer.update customer_params
render 'edit'
end
private
def customer_params
params.require(:customer).permit(:first_name, :last_name, :phone, user_attributes: [:email, :password, :password_confirmation])
end
end
customers/edit.html.erb
<%= form_for #customer, html: { class: 'checkout-attributes profile-form' } do |f| %>
<div class="row">
<div class="col-sm-6">
<span class="help-block">First Name</span>
<%= f.text_field :first_name, class: 'form-control' %>
</div>
<div class="col-sm-6">
<span class="help-block">Last Name</span>
<%= f.text_field :last_name, class: 'form-control' %>
</div>
<div class="col-sm-12">
<span class="help-block">Email</span>
<%= fields_for :user, #customer.user do |u| %>
<%= u.email_field :email, class: 'form-control' %>
<% end %>
</div>
<div class="col-sm-12">
<span class="help-block">Phone Number</span>
<%= f.text_field :phone, class: 'form-control' %>
</div>
<div class="col-sm-12">
<span class="help-block">Password</span>
<%= fields_for :user, #customer.user do |u| %>
<%= u.password_field :password, class: 'form-control' %>
<% end %>
</div>
<div class="col-sm-12">
<span class="help-block">Confirm Password</span>
<%= fields_for :user, #customer.user do |u| %>
<%= u.password_field :password_confirmation, class: 'form-control' %>
<% end %>
</div>
</div>
<% end %>
That's what I see in log
Started PATCH "/customers/560738" for 127.0.0.1 at 2015-11-04 08:15:20-0500 Processing by CustomersController#update as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"OL7NCIMKCb+LQZ+1voahUsNyD37q6sal8W7HsV4EKKP9ABMuDVGqxs0nFS2Cyo7A6XBkRLYv9tjTYH4kaHNdkA==", "image"=>"", "customer"=>{"first_name"=>"Jack", "last_name"=>"Drobazko", "phone"=>"5084427293"}, "user"=>{"email"=>"ddd#fff.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]"}, "id"=>"560738"}
Also I see that customer_params methods gives filtered (without user part) hash {"first_name"=>"Jack", "last_name"=>"Drobazko", "phone"=>"5084427293"}
So how to make it works?
According to your log file:
"image"=>"", "customer"=>{"first_name"=>"Jack",
"last_name"=>"Drobazko", "phone"=>"5084427293"},
"user"=>{"email"=>"ddd#fff.com", "password"=>"[FILTERED]",
"password_confirmation"=>" ...
All your fields_for inputs outside of a main form. Fix your form to wrap all user input in the fields_for helper.
Note about f.fields_for and simple fields_for, it's very important
<%= form_for #customer, html: { class: 'checkout-attributes profile-form' } do |f| %>
# some code here
<%= f.fields_for :user, #customer.user do |u| %>
<%= u.email_field :email, class: 'form-control' %>
<%= u.password_field :password, class: 'form-control' %>
<%= u.password_field :password_confirmation, class: 'form-control' %>
<% end %>
</div>
# some code here
Here is wonderful article from Rails core team, about using nested attributes.
I am creating an application through which a user will be able to create an account. When they create an account, in the same form they will be able to create an organization that will then be tied to their user. Once that user has created their account (and an organization) other users will be able to create an account and use an "access code" to join that organization as well. Looking at the code may explain it better.
The reason i'm posting on SO is because i have a feeling there is a better / more efficient way to do it than what i am currently doing. I'm using nested_forms (maybe not correctly) and i don't think i'm doing the associations the right way because, for example, i haven't been able to get the edit form to fill out the organization fields.
I am using sorcery for the authentication as well.
users_controller.rb
def new
#user = User.new
end
def create
#user = User.new(user_params)
if params[:user][:organization][:name].blank?
flash.now[:error] = "You must specify an organization name."
render :new
else
if params[:user][:organization][:access_code].blank?
# create new organization
#access_code = "#{SecureRandom.urlsafe_base64(16)}#{Time.now.to_i}"
#organization = Organization.create(:name => params[:user][:organization][:name], :access_code => #access_code)
#user.organization_id = #organization.id
#user.is_admin = true
else
# try and add someone to an organization
#organization = Organization.find(:all, conditions: ["name = ? AND access_code = ?", params[:user][:organization][:name], params[:user][:organization][:access_code]])
if #organization.empty?
flash.now[:error] = "No organization has been found with that name and access code."
render :new
return
else
#user.organization_id = #organization.first.id
end
end
if #user.save
user = login(#user.email, params[:user][:password])
if user
flash[:success] = "Your account has been successfully created!"
redirect_to admin_dashboard_path
end
else
flash.now[:error] = "Something went wrong! Please try again."
render :new
end
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.is_admin?
if params[:user][:organization][:name].blank? && params[:user][:organization][:name] != #user.organization.name
params[:user][:organization][:name] = #user.organization.name
end
if params[:user][:organization][:access_code].blank? && params[:user][:organization][:access_code] != #user.organization.access_code
params[:user][:organization][:access_code] = #user.organization.access_code
end
#organization = Organization.find(params[:user][:organization_id])
#organization.name = params[:user][:organization][:name]
#organization.access_code = params[:user][:organization][:access_code]
#organization.save
end
if #user.update(user_params)
flash[:success] = "Your settings have been updated!"
redirect_to edit_admin_user_path(#user.id)
else
flash.now[:error] = "Something went wrong! Please try again."
render :edit
end
end
private
def user_params
params.require(:user).permit(:organization_id, :email, :password, :password_confirmation, :full_name, :remember_me, {:organization_attributes => [:name, :website, :description, :access_code]})
end
users.rb
class User < ActiveRecord::Base
authenticates_with_sorcery!
belongs_to :organization
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates_presence_of :full_name
validates_presence_of :email
validates_uniqueness_of :email, :on => :create
validates_format_of :email, :with => VALID_EMAIL_REGEX, :on => :create
validates_presence_of :password, :on => :create
validates_confirmation_of :password
end
organization.rb
class Organization < ActiveRecord::Base
authenticates_with_sorcery!
has_many :users, :dependent => :destroy
accepts_nested_attributes_for :users
validates_presence_of :name
end
new.html.erb
<% provide(:title, 'Create a User') %>
<h1>Create a User</h1>
<p>Use the form below to create an account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control" %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control" %>
</div>
</fieldset>
<% end %>
<div class="form-actions">
<%= f.submit "Create Account", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
edit.html.erb
<% provide(:title, "Edit User: #{#user.full_name} (#{#user.organization.name})") %>
<h1>Edit User: <%= #user.full_name %> (<%= #user.organization.name %>)</h1>
<p>Use the form below to manage your account.</p>
<%= nested_form_for([:admin, #user], html: {role: "form"}) do |f| %>
<%= render "shared/error_messages", obj: #user %>
<fieldset>
<legend>User Information</legend>
<div class="form-group">
<%= f.label :full_name, "Full Name" %>
<span class="help-block">How should others see you?</span>
<%= f.text_field :full_name, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :email %>
<span class="help-block">Your email address is used as your login.</span>
<%= f.text_field :email, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password %>
<%= f.password_field :password, placeholder: "leave blank to keep password unchanged", class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :password_confirmation, "Confirm Password" %>
<%= f.password_field :password_confirmation, class: "form-control" %>
</div>
</fieldset>
<% if #user.is_admin? %>
<%= f.fields_for :organization do |o| %>
<fieldset>
<legend>Associated Organization</legend>
<div class="form-group">
<%= o.label :name, "Organization Name" %>
<span class="help-block">This is the name of the organization you are a part of.</span>
<%= o.text_field :name, class: "form-control", value: #user.organization.name %>
</div>
<div class="form-group">
<%= o.label :access_code, "Organization Access Code" %>
<span class="help-block">Leaving this field blank will setup a new organization.</span>
<%= o.text_field :access_code, class: "form-control", value: #user.organization.access_code %>
</div>
</fieldset>
<% end %>
<%= f.hidden_field :organization_id %>
<% end %>
<div class="form-actions">
<%= f.submit "Update User", class: "btn btn-primary" %>
<%= link_to "Cancel", :back, class: "text-btn" %>
</div>
<% end %>
Ok, those are all the files making it happen. Now, i have the application doing almost everything i need it to do but this doesn't feel like production-level code to me.
One issue i know i am having is that if a user types something in the organization field and nothing else the controller will create and save the organization and then render the form back with the user validation errors. I don't want it to save the organization if there are validation errors in the user model.
I'm really just asking for advice if there is a better way of doing what i am trying to do. If you can't tell exactly what i'm trying to do with this code or have any questions please let me know!
Take a look at this post: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
Of particular interest will be the section on "3. Extract Form Objects".
I'm getting a AssociationTypeMismatch Error and I'm not sure where I'm making a mistake. I'm pretty new to Rails so I'm guessing I'm making some silly mistake. I've checked my syntax and compared it against AssociationTypeMismatch Error on Ruby on Rails app
... but I still can't seem to catch the error.
Here's my models
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference
has_secure_password
validates_uniqueness_of :email
validates_presence_of :full_name
has_one :preference, :dependent => :destroy
accepts_nested_attributes_for :preference
end
class Preference < ActiveRecord::Base
attr_accessible :background_fill, :background_position, :layout
belongs_to :user
end
Here's my controller:
def new
#user = User.new
#user.build_preference
end
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
redirect_to root_url, notice: "Thanks for signing up!"
else
render "new"
end
end
Here's my view:
<%= form_for #user do |f| %>
<% if #user.errors.any? %>
<div class="error_messages">
<h2>There's an error!</h2>
<ul>
<% #user.errors.full_message.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :full_name %>
<%= f.text_field :full_name, :class => "target", :placeholder => "Your full name", :maxlength => "55", :autofocus => "autofocus" %>
<%= f.label :email %>
<%= f.email_field :email, :class => "target", :placeholder => "example#gmail.com", :maxlength => "55" %>
<%= f.label :password %>
<%= f.password_field :password, :class => "target", :placeholder => "Enter a password", :maxlength => "55" %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, :class => "target", :placeholder => "Enter your password again", :maxlength => "55" %>
<%= f.fields_for :preference do |builder| %>
<%= builder.hidden_field :layout, value: params[:layout] %>
<%= builder.hidden_field :background_fill, value: params[:background_fill] %>
<%= builder.hidden_field :background_position, value: params[:background_position] %>
<% end %>
<%= f.submit "Create an Account", :class => "button cta" %>
<% end %>
And my parameters
{"utf8"=>"✓",
"authenticity_token"=>"[redacted]=",
"user"=>{"full_name"=>"test",
"email"=>"test#example.com",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"preference"=>{"layout"=>"layout-3",
"background_fill"=>"restaurant-2",
"background_position"=>"position-center"}},
"commit"=>"Create an Account"}
Edit:
The error I get is Preference(#70124732528700) expected, got ActiveSupport::HashWithIndifferentAccess(#70124687315200). My understanding is that #user.build_preference and accepts_nested_attributes_for :preference would just work on its own.
Do I need to create a def preferences_attributes= as per?
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-fields_for
Update
Okay so I think I'm getting a bit closer. Poking around in the rails console, I figured I need to create Preference.new inside the UserController before I can pass in the hash. Since I'm not sure sure what build_preference does exactly, I'm not having any luck yet.
I've tried adding #user.preference = Preference.new above build preference and changing f.field_for :preference to f.field_for #user.preference but I'm still getting the same error.
Update 2
For anyone else that's stuck on this problem, the answer is to change f.field_for :preference to f.field_for :preference_attributes. See comment below by zetetic.
it is:
attr_accessible :preference_attributes
and in your form:
<%= f.fields_for :preference_attributes do |builder| %>
...
Give a shot.
In your User model, try to add :preference_attributes to your attr_accessible line.
class User < ActiveRecord::Base
attr_accessible :email, :full_name, :password, :password_confirmation, :preference_attributes
.. # rest of your code goes here
end