Reputation: 1410
I have a django project with the django-allauth app. I need to collect additional data from the user at signup. I came across a similar question here but unfortunately, no one answered the profile customization part.
Per the documentation provided for django-allauth
:
ACCOUNT_SIGNUP_FORM_CLASS
(=None
)A string pointing to a custom form class (e.g.
‘myapp.forms.SignupForm’
) that is used during signup to ask the user for additional input (e.g. newsletter signup, birth date). This class should implement a‘save’
method, accepting the newly signed up user as its only parameter.
I am new to django and am struggling with this. Can someone provide an example of such a custom form class? Do I need to add a model class as well with a link to the user object like this ?
Upvotes: 117
Views: 52511
Reputation: 6511
Suppose you want to ask the user for his first/last name during signup. You'll need to put these fields in your own form, like so:
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
Then, in your settings point to this form:
ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'
Note that SignupForm
cannot be defined in the same file as form overrides through ACCOUNT_FORMS
or SOCIALACCOUNT_FORMS
, because that would lead to a circular import error.
That's all.
Upvotes: 184
Reputation: 8663
#models.py
from django.conf import settings
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(default='users/default.png', upload_to='users')
fields = models.ForeignKey('Field' ,null=True ,on_delete=models.SET_NULL)
category = models.ForeignKey('Category' ,null=True ,on_delete=models.SET_NULL)
description = models.TextField()
interests = models.ManyToManyField('Interests')
...
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
...
def userprofile_receiver(sender, instance, created, *args, **kwargs):
if created:
userprofile = UserProfile.objects.create(user=instance)
else:
instance.userprofile.save()
post_save.connect(userprofile_receiver, sender=settings.AUTH_USER_MODEL)
#forms.py
class SignupForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SignupForm, self).__init__(*args, **kwargs)
self.fields['first_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter first name'})
self.fields['last_name'].widget = forms.TextInput(attrs={'placeholder': 'Enter last name'})
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
interests = forms.ModelMultipleChoiceField(widget=forms.CheckboxSelectMultiple, help_text="Choose your interests", queryset=Interests.objects.all())
image = forms.ImageField(help_text="Upload profile image ")
fields = forms.ChoiceField(help_text="Choose your fields ")
category = forms.ChoiceField(help_text="Choose your category")
class Meta:
model = UserProfile
fields = ('first_name', 'last_name', 'name', 'image', 'fields', 'category', 'description', 'phone', 'facebook', 'twitter', 'skype', 'site', 'address', 'interests' ,'biography')
widgets = {
...
'description': forms.TextInput(attrs={'placeholder': 'Your description'}),
'address': forms.TextInput(attrs={'placeholder': 'Enter address'}),
'biography': forms.TextInput(attrs={'placeholder': 'Enter biography'}),
....
}
def signup(self, request, user):
# Save your user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
user.userprofile.image = self.cleaned_data.get('image')
user.userprofile.fields = self.cleaned_data['fields']
user.userprofile.category = self.cleaned_data['category']
user.userprofile.description = self.cleaned_data['description']
interests = self.cleaned_data['interests']
user.userprofile.interests.set(interests)
user.userprofile.save()
# settings.py or base.py
ACCOUNT_SIGNUP_FORM_CLASS = 'nameApp.forms.SignupForm'
That is it. (:
Upvotes: 1
Reputation: 7212
I've tried many different tutorials and all of them is missing something, repeating unnecessary code or doing weird things, bellow follows my solution that joins all the options that I've found, it's working, I have already put it in production BUT it still not convincing me because I would expect to receive first_name and last_name inside the functions that I attached to Users create to avoid creating a profile inside the form but I couldn't, by the away I think it will help you.
Models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
nationality = models.CharField(max_length=2, choices=COUNTRIES)
gender = models.CharField(max_length=1, choices=GENDERS)
def __str__(self):
return self.user.first_name
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
Forms.py
class SignupForm(forms.ModelForm):
first_name = forms.CharField(max_length=100)
last_name = forms.CharField(max_length=100)
class Meta:
model = Profile
fields = ('first_name', 'last_name', 'nationality', 'gender')
def signup(self, request, user):
# Save your user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
user.profile.nationality = self.cleaned_data['nationality']
user.profile.gender = self.cleaned_data['gender']
user.profile.save()
Settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'apps.profile.forms.SignupForm'
Upvotes: 4
Reputation: 171
@Shreyas: The below solution may not be the cleanest, but it works. Please let me know if you have any suggestions to clean it up any further.
To add information that does not belong to the default user profile, first create a model in yourapp/models.py. Read the general django docs to learn more about it, but basicly:
from django.db import models
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
organisation = models.CharField(organisation, max_length=100, blank=True)
Then create a form in yourapp/forms.py:
from django import forms
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
organisation = forms.CharField(max_length=20, label='organisation')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
# Replace 'profile' below with the related_name on the OneToOneField linking back to the User model
up = user.profile
up.organisation = self.cleaned_data['organisation']
user.save()
up.save()
Upvotes: 7
Reputation: 201
Here's what worked for me combining a few of the other answers (none of them are 100% complete and DRY).
In yourapp/forms.py
:
from django.contrib.auth import get_user_model
from django import forms
class SignupForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name']
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
And in settings.py
:
ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'
This way it uses the model forms so that it's DRY, and uses the new def signup
. I tried putting 'myproject.myapp.forms.SignupForm'
but that resulted in a error somehow.
Upvotes: 20
Reputation: 1215
In your users/forms.py
you put:
from django.contrib.auth import get_user_model
class SignupForm(forms.ModelForm):
class Meta:
model = get_user_model()
fields = ['first_name', 'last_name']
def save(self, user):
user.save()
In settings.py you put:
ACCOUNT_SIGNUP_FORM_CLASS = 'users.forms.SignupForm'
In this way you don't break DRY principle by multiplicity User models fields definition.
Upvotes: 5
Reputation: 2052
Using the solution suggested by pennersr I was getting a DeprecationWarning:
DeprecationWarning: The custom signup form must offer a def signup(self, request, user) method DeprecationWarning)
This is because as of version 0.15 the save method has been deprecated in favour of a def signup(request, user) method.
So to solve this, the code of the example should be like this:
class SignupForm(forms.Form):
first_name = forms.CharField(max_length=30, label='Voornaam')
last_name = forms.CharField(max_length=30, label='Achternaam')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
Upvotes: 24
Reputation: 1013
Create a Profile Model with user as OneToOneField
class Profile(models.Model):
user = models.OneToOneField(User, verbose_name=_('user'), related_name='profiles')
first_name=models.CharField(_("First Name"), max_length=150)
last_name=models.CharField(_("Last Name"), max_length=150)
mugshot = ImageField(_('mugshot'), upload_to = upload_to, blank=True)
phone= models.CharField(_("Phone Number"), max_length=100)
security_question = models.ForeignKey(SecurityQuestion, related_name='security_question')
answer=models.CharField(_("Answer"), max_length=200)
recovery_number= models.CharField(_("Recovery Mobile Number"), max_length=100)
city=models.ForeignKey(City,related_name='city', blank=True, null=True, help_text=_('Select your City'))
location=models.ForeignKey(Country,related_name='location', blank=True, null=True, help_text=_('Select your Location'))
Upvotes: -10