django allauth: add more fields to signup form - django-allauth

I have django-allauth package installed I am trying to add some fields from my User model
to the signup form of allauth
from allauth.account.forms import SignupForm
class MyCustomSignupForm(SignupForm):
class Meta:
model = User
fields = ('username','email','mobile','city','district','state','country','pin_code')
But I dont see many of these fields in my signup form at /accounts/signup.
How can i do this

it works for me. There are several good guides on this topic. Ovveride the custom signup form with your own. This is done in the forms.py
from allauth.account.forms import SignupForm
class CustomignupForm(SignupForm):
def __init__(self, *args, **kwargs):
# Call the init of the parent class
super().__init__(*args, **kwargs)
self.fields[_("mobile")] = forms.CharField(required=True)
self.fields[_("city")] = forms.CharField(required=True)
self.fields[_("district")] = forms.CharField(required=True)
#"Rest of your fields"
# Put in custom signup logic
def custom_signup(self, request, user):
# Set the user's type from th form reponse
user.mobile= self.cleaned_data[_("mobile")]
user.city= self.cleaned_data[_("city")]
#"Rest of your fields"
user.save()
return user

Related

Update costume field before registration the new user in Devise Rails

I have a Rails application and using Devise for authenticating in app.
This Rails app is in API mode. I added some costume fields in devise Users model:
## Costume fields
field :ip, type: String
field :role, type: String , default: 'client'
As you can see, I wan to add IP address of user, during signup action of devise. I want to tell devise that when a you got a new "signup" request, please before adding user into the database, get user IP and add it to fields then save new user in to database like this:
{
email: 'test#example.com',
encrypted_password: "sg4rgtgesrre5erghtr5etrgtrrergre55trgf....",
ip: '1.2.2.1' // The user rote IP
}
Hope you had set up the devise controller
in your registration controller for user you can modify the script
class Users::RegistrationsController < Devise::RegistrationsController
def create
build_resource(sign_up_params)
resource.ip = '1.1.1.1'
resource.save
yield resource if block_given?
if resource.persisted?
....# rest of the create action code
end
but this wont save the ip to the database before saving.... but the ip value will be added to the user object for further saving.
You need to override the modify create action in Devise::RegistrationController for this work.
https://github.com/plataformatec/devise/blob/40f02ae69baf7e9b0449aaab2aba0d0e166f77a3/app/controllers/devise/registrations_controller.rb#L17
Your main goal is to modify the sign_up_params content. You can do it 2 ways:
You can create your your own params like
def user_sign_up_params
sign_up_params.merge ip: request.ip
end
and change the above line build_resource(user_sign_up_params)
You can add ip on place like build_resource(sign_up_params.merge(ip: request.ip))

How to make multi-step form in Rails and prompt user to sign-up after filling form?

I have model in my rails App called "Request". It consist around of the 12 fields. It has relations with user model like User has_many: request, Request belongs_to: User. For users i use Devise. My problem is next. I want that user from home page of my app has a possibility to start creating new request. It should be multi-step form like 3 steps with 4 inputs in each step. After filling all step i need to prompt user to sign up. I don't want that new row in Request table would be created until user would do Sign up. So the process should looks like user goes throw each step, we store all input data by someway, after user do sign-up we create new user in Users table and new request in Requests table. I don't want to store request until user would be registered because in this case i would have in my db a lot of requests that don't belong to any user. So how could i achieve my aim?
UPDATE:
So with users suggests and with searching i get some progress. So now i have form on one page (all 12 fields), after click on submit it goes to "create" action in RequestController where i do such code:
if current_user.nil?
session[:request] = params
# Redirect the user to register/login
redirect_to new_user_registration_path
So i get all params, store them in session[:request] and redirect to Sign-up page. Than in ApplicationController i have added code:
if session[:request].present?
# save list
#request = current_user.requests.create(session[:request]["request"])
# clear session
session[:request] = nil
which connect request parameters with current_user and create new record in db.
So now only one problem, how to make multistep for my form, which in final result would be sent to create action?
I think you can use the wicked gem for step by step wizard.
And in the first step html template keep only the input fields which you require , and in second step store the first step values in hidden field, similarly in third step you can store first and second step values in hidden field.
and in controller Just assign the values in #request variable
steps :step_one, :step_two, :step_three
def request_steps
#request = Request.new
#request.assign_attributes(params[:request])
case step
when :step_one
#add code here you want to do in step one
when :step_two
#add code here you want to do in step two
when :step_three
#add code here you want to do in step three
end
render_wizard
end
Alternate solution:
If you don't want to store in hidden field and not using wicked and have multiple steps actions then another solution is to store the request inputs in session like below:
def step1
session[:user_request] ||= {}
session[:user_request].merge!{params[request]} #merge input from step1
# render to show step2
end
def step2
session[:user_request] ||= {}
session[:user_request].merge!{params[request]} #merge input from step2
# render to show step3
end
def step3
session[:user_request] ||= {}
session[:user_request].merge!{params[request]} #merge input from step3
# render to login/signup_page
end
and after user login you can find the request data using session[:user_request] and save the request with logged in user and clear session[:user_request] like in your devise session controller
after_filter :after_login, :only => :create
def after_login
if session[:user_request]
request = current_user.requests.build(session[:user_request])
session[:user_request] = nil if request.save
end
end
Hope it would help you.
A suggestion would be to store the values on Front-end, and in the last step send all the informations. This can be achieved using javascrit, and all the variables would be naturally stored in variables. I think this is one of the the easiest ways.
Another idea is to create a few methods in controller (and routes) that gathers all the informations, it can be like (using preferable ajax, or normal post requests):
def step1
params = params[:params]
#...
end
def step2
params = params[:params]
#...
end
And then you can create a column that temporarily stores all the informations (in just on column, in format json or hash), and it just send to the right columns after the user confirmed all the infos. You can then create a cron job to delete records from temporaries columns.
These are just a few suggestions. I'm looking foward to see more ideas.

How add extra attribute in model object response in rails

I have a method to render user details after login. The method is as follows
def respond_with(resource, _opts = {})
render json:resource
I get the User object in the resource variable. I want to add a new attribute called token before rendering it. I tried resource.attributes.merge(new:"value") but its not working.
I think following will work
render json: resource.as_json.merge(new: 'value')
If you want to have new attribute for your model, you can define a attr_accessor inside that model Class

conditional validation in rails redirect

Whats the best way to implement a zip code validation feature. I'm not talking about the format of the zip code, but rather validating that a user entered zip code is one that you do business in. example here: https://doughbies.co/
For example: I only deliver to zip code 12345 so if user puts in a different zip code he gets a fail message saying "we do not deliver to your area" but if the user enters 12345 he is redirected to the shop.
I'm thinking about generating a zip code model with the acceptable zip codes as a constants in an array. Then creating a deliverable? function that matches user input to one of the zip codes in the array constant. Just don't know what methods or validations I can use for this.
Do you have a model to represent the order? If so you could have the validation there, without the need of a separate model.
class Order < ActiveRecord::Base
SHIPPABLE_ZIPS = ['12345']
validate :zip_shippable
def zip_shippable
errors.add(:zip, "cannot be shipped to") unless SHIPPABLE_ZIPS.include?(zip)
end
end
And for how to use this in the controller, take creating an order as example:
class OrdersController < ActionController::Base
def create
#order = Order.new(order_params) # "order_params" is params from the form
if #order.save
redirect orders_path # redirect the user to another page
else
render :new # render the form again, this time #order would contain the error message on zip code
end
end
end

Customizing Devise gem (Rails)

I am trying to add a new field "registration code" along with the email/password in sign up form. (i have the registration code field in a separate database, only a valid registration code/email pair will have to work with the sign up)
I could not able to find any controller for actions done by devise gem.
How do i customize devise to achieve this?
Thanks in advance.
It seems like your question basically has nothing to do with Devise itself (besides the views). To validate your registration code/email pairs, you surely need to add this as validation.
The easy way to validate registration code could be:
class User
validate :validate_registration_code
private
def validate_registration_code
reg_code = RegistrationCode.find_by_code(registration_code)
unless reg_code.email == record.email
errors.add(:registration_code, "Invalid registration code for #{record.email}")
end
end
end
You also might want to write simple custom validator:
class RegistrationCodeValidator < ActiveModel::Validator
def validate(record)
# actual reg code validation
# might look like:
reg_code = RegistrationCode.find_by_code(record.registration_code)
unless reg_code.email == record.email
record.errors[:registration_code] << "Invalid registration code for #{record.email}"
end
end
end
# in your User model
class User
# include registration code validator
include RegistrationCodeValidator
validates_with MyValidator
end
Devise keeps it's controllers behind the scenes inside the gem. If you want to add an action or modify one, you have to subclass it and do a little work in routes to get the action to your controller.
However you shouldn't need to do that to add a field. See goshakkk's answer

Resources