Reputation: 3284
I have my devise users linked to a profile model with has_one :profile
I would like to keep the initial user form very simple, with the standard username email and password. I would like then the users to be prompted the profile edit form at the first login, and I would like them to be forced to fill in some data.
at the moment my profile model is :
class Profile < ActiveRecord::Base
attr_accessible :time_zone, :telephone, :country, :opt_out,
:first_name, :last_name, :address, :city, :postcode, :birthdate,
:currency_id
belongs_to :currency
validates_presence_of :telephone, :country, :first_name, :last_name,
:address, :city, :postcode, :birthdate, :currency
belongs_to :user
end
my User model is:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
before_create :initialize_user
before_destroy :destroy_profile
has_one :profile
has_one :subscription
attr_accessible :email, :password, :password_confirmation, :remember_me,
:username, :terms
validates_acceptance_of :terms
validates_presence_of :username
private
def initialize_user
generate_profile
generate_free_subscription
end
def generate_free_subscription
subscription = Subscription.new() do |s|
s.expiration_date = nil
s.plan = :free
s.billing_name = username
s.billing_street = "unknown"
s.billing_zip = "unknown"
s.billing_city = "unknown"
s.billing_country = "unknown"
s.billing_email = email
end
if subscription.save
self.subscription = subscription
self.roles = [:free]
else
msg = "Error generating free subscription for user, #{subscription.errors.to_yaml}"
logger.error msg
raise msg
end
end
def generate_profile
p = Profile.new() do |p|
p.daily_capital_exposure = 50
p.risk_per_day = 60
p.risk_per_trade = 30
p.risk_per_week = 90
p.user_id = self.id
p.time_zone = "Rome"
end
if p.save
self.profile = p
else
msg = "Error generating profile for user #{p.errors}"
logger.error msg
raise msg
end
end
def destroy_profile
p = self.profile
t = self.trades
p.destroy
t.destroy_all
end
end
My problem is that when I create a User, the callback also creates its profile, which is missing some data and so fails creation of profile.
I wouldn't like to insert in profile temporary data just to make the profile validate correctly, because I would really like to have a nice way to force users to insert such information.
I guess my error is that I shouldn't be creating the profile at the time I create the User, but I'm not sure how else to make sure the Profile is created.
Upvotes: 1
Views: 1127
Reputation: 4546
Try something like this to create a default profile in the beginning :
class User < ActiveRecord::Base
rolify
searchkick autocomplete: [:fullname]
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_one :profile
before_create :build_default_profile
private
def build_default_profile
# build default profile instance. Will use default params.
# The foreign key to the owning User model is set automatically
build_profile
true # Always return true in callbacks as the normal 'continue' state
# Assumes that the default_profile can **always** be created.
# or
# Check the validation of the profile. If it is not valid, then
# return false from the callback. Best to use a before_validation
# if doing this. View code should check the errors of the child.
# Or add the child's errors to the User model's error array of the :base
# error item
end
end
This will create a profile when you create the user.
Also if you want to take the fullname during the registration itself, I do something like this :
#application_controller
before_action :configure_permitted_parameters, if: :devise_controller?
private
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:fullname, :email, :password, :password_confirmation) }
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:avatar, :fullname, :email, :password, :password_confirmation, :current_password) }
end
This way, you should be able to take the firstname during the registration and the then create the profile and then after the user logs in you can redirect it to the profile creation page where the user can be asked to fill in the other details.
Hope I could help.
Upvotes: 2
Reputation: 76774
You could just use the on:
option in your Profile
validations:
#app/models/profile.rb
Class Profile < ActiveRecord::Base
validates_presence_of :telephone, :country, :first_name, :last_name,
:address, :city, :postcode, :birthdate, :currency, on: :update #-> means this will not fire on create
end
--
In terms of building
your Profile model on creation of a User
, we use the following setup:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :build_profile
end
This creates a profile for the User
model upon creation of that parent model
Upvotes: 1