Devise: Add User Details in different table - ruby-on-rails

I have two models User and Person
I want to stick with the default table for the user offered by Devise to include only email and password and add User details in Person
Here is my models
class Person < ApplicationRecord
belongs_to :user
end
User Model
class User < ApplicationRecord
...
has_one :person
...
end
I also override RegistrationController.rb to look like this
class RegistrationsController < Devise::RegistrationsController
def sign_up_params
params.require(:user).permit(:first_name, :email, :password, :password_confirmation)
end
end
and here is the view
<h2>Sign up</h2>
<%= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= f.error_notification %>
<%= f.fields_for :person do |p| %>
<%= p.text_field :first_name %>
<%= p.text_field :last_name %>
<% end %>
<div class="form-inputs">
<%= f.input :email, required: true, autofocus: true %>
<%= f.input :password, required: true, hint: ("#{#minimum_password_length} characters minimum" if #minimum_password_length) %>
<%= f.input :password_confirmation, required: true %>
</div>
<div class="form-actions">
<%= f.button :submit, "Sign up" %>
</div>
<% end %>
<%= render "devise/shared/links" %>
With the code it didn't work, and it didn't update the people column while registration
How Can I get devise to add details in two models using the only one form offered by devise ?
To add email and password in Users Table
and other details e.g. first_name in People Table

Nested-Models setup for simple_form
Add accepts_nested_attributes_for to User model
class User < ActiveRecord::Base
has_one :person
accepts_nested_attributes_for :person
end
Update permit params in controller:
class RegistrationsController < Devise::RegistrationsController
def sign_up_params
params.require(:user).permit(:email, :password, :password_confirmation,
person_attributes: [:first_name, :last_name])
end
end
Change f.fields_for :person to f.simple_fields_for :person in view.
Check https://github.com/plataformatec/simple_form/wiki/Nested-Models

You need to set strong params inside your controller private method as per below:
def sign_up_params
params.require(:user).permit(:first_name, :email, :password, :password_confirmation, person_attributes: [:person_model_attributes])
end
Inside your User model add below line:
accepts_nested_attributes_for :person

Related

accepts_nested_attributes_for with validations and using find_or_create_by

I have a user model and a town model. A user belongs_to a town:
# models/user.rb
class User < ApplicationRecord
belongs_to :town
accepts_nested_attributes_for :town
validates :last_name, presence: true
end
# models/town.rb
class Town < ApplicationRecord
has_many :users
validates :name, presence: true
validates :name, uniqueness: true
end
You go to create a new user record: there is a text_box in order to put in the associated town's name. When submitted, the rails application should do the following:
use find_or_create_by. go and check if there already exists a town record with the name attribute passed in.
If a town record DOES exist with that given name attribute, just associate that existing town record to this new user record
If a town record DOES NOT exist with that given name attribute, then create a new town record with that name attribute, and associate that one to this user record
If any validations fail, do not create a user record or a town record and re-render the form.
I am having real trouble with this. This question suggests putting autosave on the belongs_to :town statement, as well as defining a method called autosave_associated_records_for_town. However: I just could not get that to work.
Appreciate it!
Please, try that solution. It works for me.
User
# user.rb
class User < ActiveRecord::Base
belongs_to :town
accepts_nested_attributes_for :town
validates :last_name, presence: true
end
Town
# town.rb
class Town < ActiveRecord::Base
has_many :users
validates :name, presence: true
end
Controller
# users_controller.rb
respond_to :html
def create
# ...
#user = User.new(user_params)
#user.town = Town.find_or_initialize_by(user_params[:town_attributes])
if #user.save
respond_with(#user)
else
render 'new'
end
end
# ...
def user_params
params.require(:user).permit(:last_name, :email, :town_id, town_attributes: [:name])
end
View
# users/_form.html.erb
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</div>
<%= f.fields_for :town, #town do |bldr| %>
<div class="field">
<%= bldr.label :name, 'Town name' %><br>
<%= bldr.text_field :name %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
UPDATE
Please consider to add validates_associated to user's validations as well.
Here is the related documentaion
class User < ActiveRecord::Base
belongs_to :town
accepts_nested_attributes_for :town
validates :last_name, presence: true
validates :town, presence: true
validates_associated :town
end
Generaly speaking, you could remove validates :town, presence: true in that case. Validations will work without it.

How can I invite users (using devise_invitable) and populate additional fields during the invite process?

For example, when I go to users/invitations/new, the only field is :email. I'd like to invite a user and, in addition to providing their email, provide:
first_name
last_name
role
company (user belongs_to company)
I created Users::InvitationsController < Devise::InvitationsController:
class Users::InvitationsController < Devise::InvitationsController
private
def resource_params
params.permit(user: [:email, :invitation_token, :role, :company_id])[:user]
end
end
and I added those fields to users/invitations/new. The invitation sends fine, but when I accept it and input a password, my validation fails saying that No role is selected (b/c of a validation).
How can I set these fields before sending the invite and have them persist and save when the invite is accepted? Thanks!
Rails 5
Here is my solution using accepts_nested_attributes_for. If your custom attributes are directly on the user model you should be able to replace profile_attributes: [:first_name, :last_name] with :first_name, :last_name, :role, :company.
Here is my controller.
class InvitationsController < Devise::InvitationsController
before_action :update_sanitized_params, only: :update
# PUT /resource/invitation
def update
respond_to do |format|
format.js do
invitation_token = Devise.token_generator.digest(resource_class, :invitation_token, update_resource_params[:invitation_token])
self.resource = resource_class.where(invitation_token: invitation_token).first
resource.skip_password = true
resource.update_attributes update_resource_params.except(:invitation_token)
end
format.html do
super
end
end
end
protected
def update_sanitized_params
devise_parameter_sanitizer.permit(:accept_invitation, keys: [:password, :password_confirmation, :invitation_token, profile_attributes: [:first_name, :last_name]])
end
end
Inside my form
<%= f.fields_for :profile do |p| %>
<div class="form-group">
<%= p.label :first_name, class: 'sr-only' %>
<%= p.text_field :first_name, autofocus: true, class: 'form-control', placeholder: 'First name' %>
</div>
<div class="form-group">
<%= p.label :last_name, class: 'sr-only' %>
<%= p.text_field :last_name, class: 'form-control', placeholder: 'Last name' %>
</div>
<% end %>
In user.rb I have
...
accepts_nested_attributes_for :profile, reject_if: proc { |attributes| attributes[:first_name].blank? }

rails - polymorphic association - one-to-one - Form is submiting nested fields with wrong name

I have User, which can be one of three types: Admin, Student, Teacher. Everyone has other attributes. I am trying polymorphic association one-to-one like this:
User
class User < ActiveRecord::Base
belongs_to :identity, :polymorphic => true
accepts_nested_attributes_for :identity, :allow_destroy => true
attr_accessible :email, :login, :remember_token,
:password_confirmation, :password, :role
end
Student
class Student < ActiveRecord::Base
attr_accessible :field
has_one :user, :as => :identity
end
Controller
def new
#user = User.new
end
def create
#user = User.new(params[:user]) # It fails here.
#user.identita.build
...
end
View
<%= form_for(#user) do |f| %>
<%= f.label :login %><br />
<%= f.text_field :login %>
<%= f.fields_for [:identity, Student.new] do |i| %>
<%= i.label :field %><br />
<%= i.textfield_select :field %>
<% end %>
<% end %>
When I submit this view (more complex, but this is the core), it sends hash like this:
{"utf8"=>"✓",
"authenticity_token"=>"...",
"user"=>{"login"=>"...",
"student"=> {"field"=>"..."}
}
So it fails on marked line in controller with:
ActiveModel::MassAssignmentSecurity::Error in UsersController#create
Can't mass-assign protected attributes: student
What am I doing wrong? Something like :as=>"student" or twisting the realationship?
Firstly, fix:
<%= f.fields_for [:identity, Student.new] do |i| %>
to:
<%= f.fields_for :identity, Student.new do |i| %>
Secondly, you are trying to use accepts_nested_attributes_for on a belongs_to relationship. This is not supported behavior AFAIK. Perhaps try moving that to the Student model:
class Student < ActiveRecord::Base
attr_accessible :field
has_one :user, :as => :identity
accepts_nested_attributes_for :user, :allow_destroy => true
end
and make the view like this:
<%= form_for(Student.new) do |i| %>
<%= i.fields_for :user, #user do |f| %>
<%= f.label :login %><br />
<%= f.text_field :login %>
<% end %>
<%= i.label :field %><br />
<%= i.textfield_select :field %>
<% end %>
From documentation of attr_accessible
attr_accessible will only set attributes in this list, to assign to
the rest of attributes you can use direct writer methods.
So once you've used attr_accessible, another attributes automatically will become a protected ones.

How do I make nested_attributes work in Rails3?

Here's my user model:
class User < ActiveRecord::Base
has_one :teacher, :dependent => :destroy
accepts_nested_attributes_for :teacher, :allow_destroy => true
attr_accessible :email, :password, :password_confirmation, :remember_me, :teacher_attributes
end
Here's my teacher model:
class Teacher < ActiveRecord::Base
belongs_to :user
attr_accessible :user_id, :first_name, :last_name
validates_presence_of :user_id, :first_name, :last_name
end
Here's my form:
<%= form_for(#user, :url => registration_path(:user)) do |user| %>
<%= user.text_field :email %>
<%= user.text_field :password %>
<%= user.text_field :password_confirmation %>
<%= user.fields_for resource.build_teacher do |t| %>
<%= t.text_field :first_name %>
<%= t.text_field :last_name %>
<%= t.text_field :phone %>
<% end %>
<%= user.submit 'Confirm' %>
<% end %>
Except that this thing won't "accept nested attributes"
My development log says:
WARNING: Can't mass-assign protected attributes: teacher
I don't know if it's related, but the form isn't generating fields inside a teacher_attributes array or anything - it's inside teacher. I'm guessing that's where my problem is, but I don't how to make it put the fields inside it. Please help.
Thanks!
Try these things:
At top of view:
<% #user.build_teacher if #user.teacher.nil? %>
For the fields for:
<%= user.fields_for :teacher do |t| %>
Also, personally, I like naming the block parameters in forms (the part |user| and |t|) as |form| (because when you're having a long day, and you see user down in the view and not form, it can confuse you!)

Using fields from an association (has_one) model with formtastic in rails

I searched and tried a lot, but I can't accomplish it as I want.. so here's my problem.
My models are:
class User < ActiveRecord::Base
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ActiveRecord::Base
attr_accessible :user_id, :form, :title, :name, :surname, :street, :housenumber, :zipcode, :place, :phone, :mobile, :fax, :url
belongs_to :user
end
In my view:
<% semantic_form_for #user do |form| %>
<%= form.inputs :login, :email, :password%>
<% form.semantic_fields_for :profile do |profile| %>
<%= profile.inputs %>
<% end %>
<%= form.buttons %>
<% end %>
My problem is that when I edit a person then it shows me the data on the profile. I would, that the fields from the profile even when creating a user are displayed.
Thanks a lot!
You should add in your view before your form.semantic_fields_for:
<% #user.build_profile unless #user.profile %>
You could do it as well in your controller new after that you create your User object.

Resources