Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pennersr/django-allauth/llms.txt
Use this file to discover all available pages before exploring further.
django-allauth provides extensive form customization options. You can override built-in forms to add custom fields, modify validation logic, or change form behavior.
Overview
Forms in django-allauth handle:
- User signup and login
- Password management (change, reset, set)
- Email address management
- Login codes and verification codes
- Social account connections
All forms can be customized by overriding them in your settings.
Configuration
Override forms using the ACCOUNT_FORMS setting:
# settings.py
ACCOUNT_FORMS = {
'login': 'myproject.forms.MyLoginForm',
'signup': 'myproject.forms.MySignupForm',
'add_email': 'myproject.forms.MyAddEmailForm',
'change_password': 'myproject.forms.MyChangePasswordForm',
'reset_password': 'myproject.forms.MyResetPasswordForm',
'reset_password_from_key': 'myproject.forms.MyResetPasswordKeyForm',
'set_password': 'myproject.forms.MySetPasswordForm',
}
Source: allauth/account/app_settings.py:464
Path: allauth.account.forms.LoginForm
Used on: Login view (account_login)
Customization
# myproject/forms.py
from allauth.account.forms import LoginForm
from django import forms
class MyLoginForm(LoginForm):
remember = forms.BooleanField(
required=False,
initial=True, # Remember by default
label="Keep me signed in"
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Customize field attributes
self.fields['login'].widget.attrs.update({
'class': 'form-control',
'placeholder': 'Email address',
'autofocus': True
})
self.fields['password'].widget.attrs.update({
'class': 'form-control',
'placeholder': 'Password'
})
def login(self, request, redirect_url=None):
# Add custom logic before login
user = self.user
# Log login attempt
import logging
logger = logging.getLogger(__name__)
logger.info(f"User {user.username} logging in")
# Call parent login method
return super().login(request, redirect_url)
# settings.py
ACCOUNT_FORMS = {
'login': 'myproject.forms.MyLoginForm',
}
Available Attributes
self.user - The User object that is logging in
Password Help Text
Customize the “Forgot your password?” link by providing a template:
{# templates/account/password_reset_help_text.html #}
<a href="{% url 'account_reset_password' %}" class="text-decoration-none">
Forgot your password? Click here to reset it.
</a>
Path: allauth.account.forms.SignupForm
Used on: Signup view (account_signup)
Basic Customization
# myproject/forms.py
from allauth.account.forms import SignupForm
from django import forms
class MySignupForm(SignupForm):
first_name = forms.CharField(
max_length=30,
label='First Name',
widget=forms.TextInput(attrs={'placeholder': 'First Name'})
)
last_name = forms.CharField(
max_length=30,
label='Last Name',
widget=forms.TextInput(attrs={'placeholder': 'Last Name'})
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Customize existing fields
self.fields['email'].widget.attrs.update({
'class': 'form-control',
'placeholder': 'Email'
})
def save(self, request):
# Ensure you call the parent class's save
user = super().save(request)
# Add custom processing
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
# You must return the original result
return user
Advanced Example with Validation
from allauth.account.forms import SignupForm
from django import forms
from django.core.exceptions import ValidationError
class MySignupForm(SignupForm):
company = forms.CharField(
max_length=100,
required=True,
label='Company Name'
)
department = forms.ChoiceField(
choices=[
('engineering', 'Engineering'),
('sales', 'Sales'),
('marketing', 'Marketing'),
('other', 'Other'),
],
required=True,
label='Department'
)
agree_to_terms = forms.BooleanField(
required=True,
label='I agree to the Terms of Service and Privacy Policy'
)
def clean_email(self):
email = self.cleaned_data['email']
# Only allow company email addresses
if not email.endswith('@company.com'):
raise ValidationError(
"Please use your company email address."
)
return email
def clean(self):
cleaned_data = super().clean()
# Add cross-field validation
company = cleaned_data.get('company')
email = cleaned_data.get('email')
if company and email:
email_domain = email.split('@')[1]
if 'company.com' in email_domain:
# Validate company name matches email
pass
return cleaned_data
def save(self, request):
user = super().save(request)
# Save additional fields to user profile
user.company = self.cleaned_data['company']
user.department = self.cleaned_data['department']
user.save()
return user
# settings.py
ACCOUNT_FORMS = {
'signup': 'myproject.forms.MySignupForm',
}
Alternatively, use ACCOUNT_SIGNUP_FORM_CLASS to specify just additional fields:
# myproject/forms.py
from django import forms
class CustomSignupForm(forms.Form):
"""Additional fields for signup."""
phone = forms.CharField(max_length=20, required=False)
date_of_birth = forms.DateField(required=False)
def signup(self, request, user):
"""Save the additional fields."""
user.phone = self.cleaned_data['phone']
user.date_of_birth = self.cleaned_data['date_of_birth']
user.save()
# settings.py
ACCOUNT_SIGNUP_FORM_CLASS = 'myproject.forms.CustomSignupForm'
Source: allauth/account/app_settings.py:314
Path: allauth.account.forms.AddEmailForm
Used on: Email management view (account_email)
Customization
from allauth.account.forms import AddEmailForm
from django.core.exceptions import ValidationError
class MyAddEmailForm(AddEmailForm):
def clean_email(self):
email = super().clean_email()
# Only allow certain email domains
allowed_domains = ['company.com', 'corp.company.com']
domain = email.split('@')[1]
if domain not in allowed_domains:
raise ValidationError(
f"Email must be from one of: {', '.join(allowed_domains)}"
)
return email
def save(self, request):
# Ensure you call the parent class's save
email_address_obj = super().save(request)
# Add custom processing
import logging
logger = logging.getLogger(__name__)
logger.info(
f"User {request.user.username} added email {email_address_obj.email}"
)
# You must return the original result
return email_address_obj
Available Attributes
self.user - The User object that is logged in
Path: allauth.account.forms.ChangePasswordForm
Used on: Change password view (account_change_password)
Customization
from allauth.account.forms import ChangePasswordForm
from django.core.exceptions import ValidationError
class MyChangePasswordForm(ChangePasswordForm):
def clean_password1(self):
password1 = self.cleaned_data.get('password1')
# Check against user's old passwords
user = self.user
if user.check_password(password1):
raise ValidationError(
"New password cannot be the same as your current password."
)
return password1
def save(self):
# Ensure you call the parent class's save
super().save()
# Add custom processing
import logging
logger = logging.getLogger(__name__)
logger.info(f"User {self.user.username} changed password")
# Send notification email
from django.core.mail import send_mail
send_mail(
'Password Changed',
'Your password was recently changed.',
'noreply@example.com',
[self.user.email],
)
Available Attributes
self.user - The User object that is logged in
Path: allauth.account.forms.SetPasswordForm
Used on: Set password view (account_set_password)
Used when a user doesn’t have a usable password (e.g., signed up via social auth).
Customization
from allauth.account.forms import SetPasswordForm
class MySetPasswordForm(SetPasswordForm):
def save(self):
super().save()
# Mark user as having completed password setup
self.user.has_password = True
self.user.save()
# Send welcome email
from django.core.mail import send_mail
send_mail(
'Password Set Successfully',
'You can now use your password to log in.',
'noreply@example.com',
[self.user.email],
)
Available Attributes
self.user - The User object that is logged in
Path: allauth.account.forms.ResetPasswordForm
Used on: Password reset view (account_reset_password)
Customization
from allauth.account.forms import ResetPasswordForm
class MyResetPasswordForm(ResetPasswordForm):
def clean_email(self):
email = super().clean_email()
# Add custom validation
# Check if email is from allowed domain
return email
def save(self, request, **kwargs):
# Ensure you call the parent class's save
email_address = super().save(request, **kwargs)
# Add custom processing
import logging
logger = logging.getLogger(__name__)
logger.info(f"Password reset requested for {email_address}")
# Ensure you return the original result
return email_address
Available Attributes
self.users - List of all possible User objects with matching email address
Path: allauth.account.forms.ResetPasswordKeyForm
Used on: Password reset confirmation view
Customization
from allauth.account.forms import ResetPasswordKeyForm
class MyResetPasswordKeyForm(ResetPasswordKeyForm):
def clean_password1(self):
password1 = self.cleaned_data.get('password1')
# Custom password validation
if len(password1) < 12:
raise ValidationError(
"Password must be at least 12 characters long."
)
return password1
def save(self):
# Add custom processing before save
import logging
logger = logging.getLogger(__name__)
logger.info(f"User {self.user.username} reset password")
# Ensure you call the parent class's save
super().save()
Available Attributes
self.user - The User object
Customize social account forms using SOCIALACCOUNT_FORMS:
# settings.py
SOCIALACCOUNT_FORMS = {
'signup': 'myproject.forms.MySocialSignupForm',
'disconnect': 'myproject.forms.MyDisconnectForm',
}
Source: allauth/socialaccount/app_settings.py:130
from allauth.socialaccount.forms import SignupForm
from django import forms
class MySocialSignupForm(SignupForm):
newsletter = forms.BooleanField(
required=False,
label='Subscribe to newsletter'
)
def save(self, request):
user = super().save(request)
# Save newsletter preference
if self.cleaned_data['newsletter']:
# Subscribe to newsletter
pass
return user
Field Configuration
Control which fields appear in the signup form:
# settings.py
ACCOUNT_SIGNUP_FIELDS = {
'email': {'required': True},
'username': {'required': True},
'password1': {'required': True},
'password2': {'required': True},
}
Source: allauth/account/app_settings.py:328
Customize form widgets:
from allauth.account.forms import SignupForm
from django import forms
class MySignupForm(SignupForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Add CSS classes
for field_name, field in self.fields.items():
field.widget.attrs['class'] = 'form-control'
# Add placeholders
self.fields['email'].widget.attrs['placeholder'] = 'you@example.com'
self.fields['username'].widget.attrs['placeholder'] = 'Choose a username'
# Add help text
self.fields['password1'].help_text = (
'Must be at least 8 characters with letters and numbers'
)
Complete Example
Here’s a comprehensive example with multiple customized forms:
# myproject/forms.py
from allauth.account.forms import (
LoginForm, SignupForm, ChangePasswordForm, ResetPasswordForm
)
from django import forms
from django.core.exceptions import ValidationError
import logging
logger = logging.getLogger(__name__)
class MyLoginForm(LoginForm):
"""Custom login form with enhanced styling."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['login'].widget.attrs.update({
'class': 'form-control form-control-lg',
'placeholder': 'Email or Username',
'autofocus': True
})
self.fields['password'].widget.attrs.update({
'class': 'form-control form-control-lg',
'placeholder': 'Password'
})
def login(self, request, redirect_url=None):
logger.info(f"User {self.user.username} logging in")
return super().login(request, redirect_url)
class MySignupForm(SignupForm):
"""Custom signup form with additional fields."""
first_name = forms.CharField(
max_length=30,
required=True,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'First Name'
})
)
last_name = forms.CharField(
max_length=30,
required=True,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Last Name'
})
)
company = forms.CharField(
max_length=100,
required=True,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Company Name'
})
)
agree_to_terms = forms.BooleanField(
required=True,
label='I agree to the Terms of Service',
error_messages={
'required': 'You must agree to the terms to continue'
}
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Apply styling to all fields
for field_name, field in self.fields.items():
if field_name not in ['agree_to_terms']:
field.widget.attrs['class'] = 'form-control'
def clean_email(self):
email = self.cleaned_data['email']
# Only allow corporate emails
if email.endswith(('@gmail.com', '@yahoo.com', '@hotmail.com')):
raise ValidationError(
"Please use your corporate email address."
)
return email
def save(self, request):
user = super().save(request)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.company = self.cleaned_data['company']
user.save()
logger.info(f"New user registered: {user.username}")
return user
class MyChangePasswordForm(ChangePasswordForm):
"""Custom change password form with additional validation."""
def clean_password1(self):
password1 = self.cleaned_data.get('password1')
# Can't reuse current password
if self.user.check_password(password1):
raise ValidationError(
"New password must be different from current password."
)
return password1
def save(self):
super().save()
logger.info(f"User {self.user.username} changed password")
# Send notification email
from allauth.account.adapter import get_adapter
adapter = get_adapter()
adapter.send_notification_mail(
'account/email/password_changed',
self.user
)
class MyResetPasswordForm(ResetPasswordForm):
"""Custom reset password form with logging."""
def save(self, request, **kwargs):
email = super().save(request, **kwargs)
logger.info(f"Password reset requested for {email}")
return email
# settings.py
ACCOUNT_FORMS = {
'login': 'myproject.forms.MyLoginForm',
'signup': 'myproject.forms.MySignupForm',
'change_password': 'myproject.forms.MyChangePasswordForm',
'reset_password': 'myproject.forms.MyResetPasswordForm',
}
# Signup field configuration
ACCOUNT_SIGNUP_FIELDS = {
'email': {'required': True},
'username': {'required': True},
'password1': {'required': True},
'password2': {'required': True},
}
Validation Helpers
Using Django Validators
from django.core.validators import RegexValidator
from allauth.account.forms import SignupForm
from django import forms
class MySignupForm(SignupForm):
phone = forms.CharField(
validators=[
RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
)
]
)
Custom Validation Methods
class MySignupForm(SignupForm):
def clean_username(self):
username = self.cleaned_data['username']
# No profanity
bad_words = ['spam', 'admin', 'root']
if username.lower() in bad_words:
raise ValidationError("This username is not allowed.")
return username
def clean(self):
"""Cross-field validation."""
cleaned_data = super().clean()
password1 = cleaned_data.get('password1')
username = cleaned_data.get('username')
if password1 and username:
# Password can't contain username
if username.lower() in password1.lower():
raise ValidationError(
"Password cannot contain your username."
)
return cleaned_data
Best Practices
- Always call super() - Ensure parent methods are called to maintain functionality
- Return values - Form save methods must return the expected object
- Log changes - Log important events like password changes
- Validate early - Use
clean_* methods for field-specific validation
- Use error_messages - Provide user-friendly error messages
- Style consistently - Apply consistent CSS classes and styling
- Test thoroughly - Test all validation paths and edge cases