I am using Devise to build a registration/authentication system into my application.
Having looked at quite a few resources for adding information to the devise model (eg username, biography, avatar URL, et cetera..) [resources include Jaco Pretorius' website , this (badly formed) SO question , and this SO question .
That's all fine and well -- it works. But my problem is that it's saving to the User model, which, according to database normalizations ( also referencing this SO question ), it should in fact be saving to a sub-model of User which is connected via has_one
and belongs_to
.
Thus far, I have created a User
model via Devise. I have also created a UserProfile
model via the rails generate
script.
user.rb (for reference)
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
end
user_profile.rb
class UserProfile < ActiveRecord::Base
belongs_to :user
end
timestamp_create_user_profiles.rb
class CreateUserProfiles < ActiveRecord::Migration
def change
create_table :user_profiles do |t|
t.string :username, null: false
t.string :biography, default: ""
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :user_profiles, [:user_id, :username]
end
end
My question, now, is, how does one collect the information for both of these models and ensure, via the devise registration form, that it all ends up in the right places?
I've seen resources about creating state machines ( AASM , and the answer to this SO question . I've also seen information about creating a wizard with WICKED , and an article on the same topic.
These all seem too complicated for my use-case. Is there some way to simply separate the inputs with devise and make sure the end up in the right place?
I think, instead of simply commenting on an answer that led me to the final answer , I'll archive the answer here in case someone in the future is trying to also find this answer:
I will be assuming that you have some sort of setup as I do above.
First step is you need to modify your User controller to accept_nested_attributes_for
the profile reference as well as add a utility method to the model so when requested in code, the application can either retrieve the built profile model or build one.
The user model ends up looking like so:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable, :confirmable, :recoverable, :rememberable, :trackable, :validatable
has_one :user_profile, dependent: :destroy
accepts_nested_attributes_for :user_profile
def user_profile
super || build_user_profile
end
end
Secondly, you will need to modify your sign up/account_update form to be able to pass the attributes for this secondary model into the controller and eventually to be able to build the profile for the parent model.
You can do this by using f.fields_for
.
Add something like this to your form:
<%= f.fields_for :user_profile do |user_profile_form| %>
<%= user_profile_form.text_field :attribute %>
<% end %>
An example of this in my specific case is:
<%= f.fields_for :user_profile do |user_profile_form| %>
<div class="form-group">
<%= user_profile_form.text_field :username, class: "form-control", placeholder: "Username" %>
</div>
<% end %>
Finally, you will need to tell Devise that it should accept this new hash of arguments and pass it to the model.
If you have created your own RegistrationsController and extended Devise's, it should look similar to this:
class RegistrationsController < Devise::RegistrationsController
private
def sign_up_params
params.require(:user).permit(:email, :password, user_profile_attributes: :username)
end
end
(Of course, make the proper changes for your specific use-case.)
If you have simply added the Devise sanitization methods to your application controller, it should look similar to this:
class ApplicationController < ActionController::Base
before_filter :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) {|u|
u.permit(:email, :password, user_profile_attributes: :username)}
end
end
(Again, make the proper changes for your specific use-case.)
A small note on user_profile_attributes: :username
: Note this is a hash , of course. If you have more than one attribute you are passing in, say, as an account_update
(hint hint), you will need to pass them like so user_profile_attributes: [:attribute_1, :attribute_2, :attribute_3]
.
Please check out the RailsCasts.com web-site.
There are a couple of interesting railscasts about nested model forms:
http://railscasts.com/episodes/196-nested-model-form-part-1
http://railscasts.com/episodes/197-nested-model-form-part-2
http://railscasts.com/episodes/196-nested-model-form-revised
Also check out accepts_nested_attributes_for
Or check out this question: Profile model for Devise users?
Also note that for Devise 4.2 the '.for' method for the devise_parameter_sanitizer is deprecated in favor of '.permit'
From the documentation :
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_in) do |user_params|
user_params.permit(:username, :email)
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.