Django's default User model works well for many applications, but often you'll need to customize user authentication to fit your specific requirements. Understanding how to create and implement custom user models enables you to build authentication systems tailored to your application's unique needs.
# Reasons to create a custom user model
class CustomUserModelDecision:
"""Guide for deciding when to use custom user models"""
@staticmethod
def scenarios_requiring_custom_user():
"""Scenarios that require a custom user model"""
scenarios = {
'email_as_username': {
'description': 'Use email address instead of username for login',
'example': 'Modern web applications often prefer email login',
'complexity': 'Medium'
},
'additional_required_fields': {
'description': 'Add required fields to user registration',
'example': 'Phone number, date of birth, organization',
'complexity': 'Low'
},
'remove_unused_fields': {
'description': 'Remove fields like username if not needed',
'example': 'Email-only authentication systems',
'complexity': 'Medium'
},
'different_authentication': {
'description': 'Use different authentication methods',
'example': 'Social security number, employee ID',
'complexity': 'High'
},
'custom_permissions': {
'description': 'Implement custom permission logic',
'example': 'Role-based permissions, hierarchical access',
'complexity': 'High'
}
}
return scenarios
@staticmethod
def alternatives_to_custom_user():
"""Alternatives to creating a custom user model"""
alternatives = {
'user_profile': {
'description': 'Create a separate profile model linked to User',
'use_case': 'Adding optional fields without changing authentication',
'pros': ['Simpler', 'No migration complexity', 'Backward compatible'],
'cons': ['Extra database join', 'Cannot change required fields']
},
'proxy_model': {
'description': 'Create a proxy model for custom methods',
'use_case': 'Adding methods without changing database structure',
'pros': ['No database changes', 'Easy to implement'],
'cons': ['Cannot add fields', 'Limited customization']
},
'extend_user': {
'description': 'Extend User with additional models',
'use_case': 'Complex user data that doesn\'t fit in one model',
'pros': ['Flexible', 'Maintains User model compatibility'],
'cons': ['More complex queries', 'Multiple models to manage']
}
}
return alternatives
# Example: User profile approach (alternative to custom user)
class UserProfile(models.Model):
"""User profile model as alternative to custom user"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
# Additional fields
phone_number = models.CharField(max_length=15, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
website = models.URLField(blank=True)
avatar = models.ImageField(upload_to='avatars/', blank=True)
# Preferences
email_notifications = models.BooleanField(default=True)
theme_preference = models.CharField(
max_length=10,
choices=[('light', 'Light'), ('dark', 'Dark')],
default='light'
)
language = models.CharField(max_length=10, default='en')
timezone = models.CharField(max_length=50, default='UTC')
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.username}'s Profile"
def get_full_name(self):
"""Get user's full name"""
return self.user.get_full_name()
def get_age(self):
"""Calculate user's age"""
if self.date_of_birth:
today = timezone.now().date()
return today.year - self.date_of_birth.year - (
(today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day)
)
return None
# Signal to create profile automatically
from django.db.models.signals import post_save
from django.dispatch import receiver
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
"""Create user profile when user is created"""
if created:
UserProfile.objects.create(user=instance)
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
"""Save user profile when user is saved"""
if hasattr(instance, 'profile'):
instance.profile.save()
# Custom user model using email as username
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.core.mail import send_mail
from django.utils.translation import gettext_lazy as _
class EmailUserManager(BaseUserManager):
"""Manager for email-based user model"""
def create_user(self, email, password=None, **extra_fields):
"""Create and return a regular user with email and password"""
if not email:
raise ValueError(_('The Email field must be set'))
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password=None, **extra_fields):
"""Create and return a superuser with email and password"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(email, password, **extra_fields)
class EmailUser(AbstractBaseUser, PermissionsMixin):
"""Custom user model that uses email instead of username"""
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=150, blank=True)
last_name = models.CharField(_('last name'), max_length=150, blank=True)
is_staff = models.BooleanField(
_('staff status'),
default=False,
help_text=_('Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
_('active'),
default=True,
help_text=_(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
objects = EmailUserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def clean(self):
super().clean()
self.email = self.__class__.objects.normalize_email(self.email)
def get_full_name(self):
"""Return the first_name plus the last_name, with a space in between"""
full_name = f'{self.first_name} {self.last_name}'
return full_name.strip()
def get_short_name(self):
"""Return the short name for the user"""
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user"""
send_mail(subject, message, from_email, [self.email], **kwargs)
# Settings configuration for custom user model
# In settings.py:
# AUTH_USER_MODEL = 'myapp.EmailUser'
# Extended user model with comprehensive fields
class ExtendedUser(AbstractUser):
"""Extended user model with additional fields"""
# Contact information
phone_number = models.CharField(
_('phone number'),
max_length=15,
blank=True,
help_text=_('Phone number in international format')
)
# Personal information
date_of_birth = models.DateField(_('date of birth'), null=True, blank=True)
gender = models.CharField(
_('gender'),
max_length=10,
choices=[
('M', _('Male')),
('F', _('Female')),
('O', _('Other')),
('N', _('Prefer not to say')),
],
blank=True
)
# Address information
address_line_1 = models.CharField(_('address line 1'), max_length=255, blank=True)
address_line_2 = models.CharField(_('address line 2'), max_length=255, blank=True)
city = models.CharField(_('city'), max_length=100, blank=True)
state = models.CharField(_('state/province'), max_length=100, blank=True)
postal_code = models.CharField(_('postal code'), max_length=20, blank=True)
country = models.CharField(_('country'), max_length=100, blank=True)
# Professional information
job_title = models.CharField(_('job title'), max_length=100, blank=True)
company = models.CharField(_('company'), max_length=100, blank=True)
department = models.CharField(_('department'), max_length=100, blank=True)
# Profile information
bio = models.TextField(_('biography'), max_length=500, blank=True)
website = models.URLField(_('website'), blank=True)
avatar = models.ImageField(_('avatar'), upload_to='avatars/', blank=True)
# Preferences
language = models.CharField(
_('language'),
max_length=10,
choices=[
('en', _('English')),
('es', _('Spanish')),
('fr', _('French')),
('de', _('German')),
],
default='en'
)
timezone = models.CharField(
_('timezone'),
max_length=50,
default='UTC',
help_text=_('User\'s preferred timezone')
)
theme_preference = models.CharField(
_('theme preference'),
max_length=10,
choices=[
('light', _('Light')),
('dark', _('Dark')),
('auto', _('Auto')),
],
default='light'
)
# Notification preferences
email_notifications = models.BooleanField(_('email notifications'), default=True)
sms_notifications = models.BooleanField(_('SMS notifications'), default=False)
push_notifications = models.BooleanField(_('push notifications'), default=True)
marketing_emails = models.BooleanField(_('marketing emails'), default=False)
# Security settings
two_factor_enabled = models.BooleanField(_('two-factor authentication'), default=False)
totp_secret = models.CharField(_('TOTP secret'), max_length=32, blank=True)
backup_codes = models.JSONField(_('backup codes'), default=list, blank=True)
# Account status
email_verified = models.BooleanField(_('email verified'), default=False)
phone_verified = models.BooleanField(_('phone verified'), default=False)
account_locked = models.BooleanField(_('account locked'), default=False)
lock_reason = models.CharField(_('lock reason'), max_length=255, blank=True)
# Timestamps
last_password_change = models.DateTimeField(_('last password change'), null=True, blank=True)
last_profile_update = models.DateTimeField(_('last profile update'), auto_now=True)
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_address(self):
"""Get formatted full address"""
address_parts = [
self.address_line_1,
self.address_line_2,
self.city,
self.state,
self.postal_code,
self.country
]
return ', '.join(filter(None, address_parts))
def get_age(self):
"""Calculate user's age"""
if self.date_of_birth:
today = timezone.now().date()
return today.year - self.date_of_birth.year - (
(today.month, today.day) < (self.date_of_birth.month, self.date_of_birth.day)
)
return None
def get_display_name(self):
"""Get display name for user"""
full_name = self.get_full_name()
return full_name if full_name.strip() else self.username
def has_complete_profile(self):
"""Check if user has completed their profile"""
required_fields = [
'first_name', 'last_name', 'email', 'phone_number'
]
for field in required_fields:
if not getattr(self, field):
return False
return True
def get_avatar_url(self):
"""Get avatar URL or default"""
if self.avatar:
return self.avatar.url
return f"https://www.gravatar.com/avatar/{hashlib.md5(self.email.lower().encode()).hexdigest()}?d=identicon&s=150"
def enable_two_factor(self):
"""Enable two-factor authentication"""
if not self.totp_secret:
import pyotp
self.totp_secret = pyotp.random_base32()
self.two_factor_enabled = True
self.save(update_fields=['totp_secret', 'two_factor_enabled'])
def disable_two_factor(self):
"""Disable two-factor authentication"""
self.two_factor_enabled = False
self.totp_secret = ''
self.backup_codes = []
self.save(update_fields=['two_factor_enabled', 'totp_secret', 'backup_codes'])
def generate_backup_codes(self, count=10):
"""Generate backup codes for 2FA"""
import secrets
codes = []
for _ in range(count):
code = '-'.join([
secrets.token_hex(2).upper()
for _ in range(3)
])
codes.append(code)
self.backup_codes = codes
self.save(update_fields=['backup_codes'])
return codes
# Role-based user model with hierarchical permissions
class Role(models.Model):
"""Role model for role-based access control"""
name = models.CharField(_('name'), max_length=50, unique=True)
description = models.TextField(_('description'), blank=True)
permissions = models.ManyToManyField(
'auth.Permission',
verbose_name=_('permissions'),
blank=True,
)
# Hierarchy support
parent_role = models.ForeignKey(
'self',
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='child_roles',
verbose_name=_('parent role')
)
# Role properties
is_system_role = models.BooleanField(_('system role'), default=False)
is_active = models.BooleanField(_('active'), default=True)
created_at = models.DateTimeField(_('created at'), auto_now_add=True)
updated_at = models.DateTimeField(_('updated at'), auto_now=True)
class Meta:
verbose_name = _('role')
verbose_name_plural = _('roles')
ordering = ['name']
def __str__(self):
return self.name
def get_all_permissions(self):
"""Get all permissions including inherited from parent roles"""
permissions = set(self.permissions.all())
# Add permissions from parent roles
current_role = self.parent_role
while current_role:
permissions.update(current_role.permissions.all())
current_role = current_role.parent_role
return permissions
def has_permission(self, permission):
"""Check if role has specific permission"""
return permission in self.get_all_permissions()
class RoleBasedUser(AbstractUser):
"""User model with role-based access control"""
# Replace groups with roles
roles = models.ManyToManyField(
Role,
verbose_name=_('roles'),
blank=True,
help_text=_('The roles this user belongs to.'),
related_name='users'
)
# Additional fields
employee_id = models.CharField(_('employee ID'), max_length=20, unique=True, null=True, blank=True)
department = models.CharField(_('department'), max_length=100, blank=True)
manager = models.ForeignKey(
'self',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='subordinates',
verbose_name=_('manager')
)
# Account status
is_temporary = models.BooleanField(_('temporary account'), default=False)
expires_at = models.DateTimeField(_('expires at'), null=True, blank=True)
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_all_permissions(self, obj=None):
"""Get all permissions from roles"""
permissions = set()
for role in self.roles.filter(is_active=True):
permissions.update(
perm.content_type.app_label + '.' + perm.codename
for perm in role.get_all_permissions()
)
# Add user-specific permissions
permissions.update(super().get_all_permissions(obj))
return permissions
def has_perm(self, perm, obj=None):
"""Check if user has permission"""
if self.is_active and self.is_superuser:
return True
return perm in self.get_all_permissions(obj)
def has_role(self, role_name):
"""Check if user has specific role"""
return self.roles.filter(name=role_name, is_active=True).exists()
def add_role(self, role_name):
"""Add role to user"""
try:
role = Role.objects.get(name=role_name, is_active=True)
self.roles.add(role)
return True
except Role.DoesNotExist:
return False
def remove_role(self, role_name):
"""Remove role from user"""
try:
role = Role.objects.get(name=role_name)
self.roles.remove(role)
return True
except Role.DoesNotExist:
return False
def get_subordinates(self):
"""Get all subordinates in hierarchy"""
subordinates = []
def collect_subordinates(manager):
direct_subordinates = manager.subordinates.all()
subordinates.extend(direct_subordinates)
for subordinate in direct_subordinates:
collect_subordinates(subordinate)
collect_subordinates(self)
return subordinates
def is_manager_of(self, user):
"""Check if this user is manager of another user"""
return user in self.get_subordinates()
def clean(self):
"""Validate user data"""
super().clean()
# Check account expiry
if self.is_temporary and not self.expires_at:
raise ValidationError(_('Temporary accounts must have an expiry date.'))
# Prevent circular manager relationships
if self.manager:
current = self.manager
while current:
if current == self:
raise ValidationError(_('Circular manager relationship detected.'))
current = current.manager
# Forms for custom user models
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
class EmailUserCreationForm(UserCreationForm):
"""Form for creating EmailUser instances"""
class Meta:
model = EmailUser
fields = ('email', 'first_name', 'last_name')
def clean_email(self):
"""Validate email"""
email = self.cleaned_data['email']
if EmailUser.objects.filter(email=email).exists():
raise forms.ValidationError(_('A user with this email already exists.'))
return email
class EmailUserChangeForm(UserChangeForm):
"""Form for changing EmailUser instances"""
class Meta:
model = EmailUser
fields = ('email', 'first_name', 'last_name', 'is_active', 'is_staff')
class ExtendedUserCreationForm(UserCreationForm):
"""Form for creating ExtendedUser instances"""
email = forms.EmailField(required=True)
first_name = forms.CharField(max_length=150, required=True)
last_name = forms.CharField(max_length=150, required=True)
phone_number = forms.CharField(max_length=15, required=False)
class Meta:
model = ExtendedUser
fields = (
'username', 'email', 'first_name', 'last_name',
'phone_number', 'password1', 'password2'
)
def clean_email(self):
"""Validate email uniqueness"""
email = self.cleaned_data['email']
if ExtendedUser.objects.filter(email=email).exists():
raise forms.ValidationError(_('A user with this email already exists.'))
return email
def save(self, commit=True):
"""Save user with additional fields"""
user = super().save(commit=False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.phone_number = self.cleaned_data['phone_number']
if commit:
user.save()
return user
class RoleBasedUserCreationForm(UserCreationForm):
"""Form for creating RoleBasedUser instances"""
roles = forms.ModelMultipleChoiceField(
queryset=Role.objects.filter(is_active=True),
widget=forms.CheckboxSelectMultiple,
required=False
)
class Meta:
model = RoleBasedUser
fields = (
'username', 'email', 'first_name', 'last_name',
'employee_id', 'department', 'manager', 'roles'
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Exclude system roles from selection
self.fields['roles'].queryset = Role.objects.filter(
is_active=True,
is_system_role=False
)
def save(self, commit=True):
"""Save user with roles"""
user = super().save(commit)
if commit:
user.roles.set(self.cleaned_data['roles'])
return user
# Profile update forms
class UserProfileUpdateForm(forms.ModelForm):
"""Form for updating user profile information"""
class Meta:
model = ExtendedUser
fields = [
'first_name', 'last_name', 'email', 'phone_number',
'date_of_birth', 'gender', 'bio', 'website',
'address_line_1', 'address_line_2', 'city', 'state',
'postal_code', 'country', 'job_title', 'company',
'department', 'language', 'timezone', 'theme_preference'
]
widgets = {
'date_of_birth': forms.DateInput(attrs={'type': 'date'}),
'bio': forms.Textarea(attrs={'rows': 4}),
'gender': forms.Select(),
'language': forms.Select(),
'theme_preference': forms.Select(),
}
def clean_email(self):
"""Validate email uniqueness"""
email = self.cleaned_data['email']
# Exclude current user from uniqueness check
if (ExtendedUser.objects.filter(email=email)
.exclude(pk=self.instance.pk).exists()):
raise forms.ValidationError(_('A user with this email already exists.'))
return email
class NotificationPreferencesForm(forms.ModelForm):
"""Form for updating notification preferences"""
class Meta:
model = ExtendedUser
fields = [
'email_notifications', 'sms_notifications',
'push_notifications', 'marketing_emails'
]
widgets = {
'email_notifications': forms.CheckboxInput(),
'sms_notifications': forms.CheckboxInput(),
'push_notifications': forms.CheckboxInput(),
'marketing_emails': forms.CheckboxInput(),
}
# Admin configuration for custom user models
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext_lazy as _
@admin.register(EmailUser)
class EmailUserAdmin(BaseUserAdmin):
"""Admin for EmailUser model"""
form = EmailUserChangeForm
add_form = EmailUserCreationForm
list_display = ('email', 'first_name', 'last_name', 'is_staff', 'date_joined')
list_filter = ('is_staff', 'is_superuser', 'is_active', 'date_joined')
search_fields = ('email', 'first_name', 'last_name')
ordering = ('email',)
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name')}),
(_('Permissions'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions'),
}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'first_name', 'last_name', 'password1', 'password2'),
}),
)
@admin.register(ExtendedUser)
class ExtendedUserAdmin(BaseUserAdmin):
"""Admin for ExtendedUser model"""
list_display = (
'username', 'email', 'first_name', 'last_name',
'is_staff', 'email_verified', 'date_joined'
)
list_filter = (
'is_staff', 'is_superuser', 'is_active', 'email_verified',
'two_factor_enabled', 'gender', 'language', 'date_joined'
)
search_fields = ('username', 'email', 'first_name', 'last_name', 'phone_number')
fieldsets = BaseUserAdmin.fieldsets + (
(_('Contact Information'), {
'fields': ('phone_number', 'website')
}),
(_('Personal Information'), {
'fields': ('date_of_birth', 'gender', 'bio', 'avatar')
}),
(_('Address'), {
'fields': (
'address_line_1', 'address_line_2', 'city',
'state', 'postal_code', 'country'
),
'classes': ('collapse',)
}),
(_('Professional Information'), {
'fields': ('job_title', 'company', 'department'),
'classes': ('collapse',)
}),
(_('Preferences'), {
'fields': ('language', 'timezone', 'theme_preference'),
'classes': ('collapse',)
}),
(_('Notifications'), {
'fields': (
'email_notifications', 'sms_notifications',
'push_notifications', 'marketing_emails'
),
'classes': ('collapse',)
}),
(_('Security'), {
'fields': (
'email_verified', 'phone_verified', 'two_factor_enabled',
'account_locked', 'lock_reason'
),
'classes': ('collapse',)
}),
(_('Timestamps'), {
'fields': ('last_password_change', 'last_profile_update'),
'classes': ('collapse',)
}),
)
readonly_fields = ('last_profile_update', 'totp_secret', 'backup_codes')
@admin.register(Role)
class RoleAdmin(admin.ModelAdmin):
"""Admin for Role model"""
list_display = ('name', 'parent_role', 'is_system_role', 'is_active', 'created_at')
list_filter = ('is_system_role', 'is_active', 'created_at')
search_fields = ('name', 'description')
fieldsets = (
(None, {'fields': ('name', 'description', 'parent_role')}),
(_('Permissions'), {'fields': ('permissions',)}),
(_('Settings'), {'fields': ('is_system_role', 'is_active')}),
)
filter_horizontal = ('permissions',)
@admin.register(RoleBasedUser)
class RoleBasedUserAdmin(BaseUserAdmin):
"""Admin for RoleBasedUser model"""
list_display = (
'username', 'email', 'first_name', 'last_name',
'employee_id', 'department', 'is_staff', 'is_temporary'
)
list_filter = (
'is_staff', 'is_superuser', 'is_active', 'is_temporary',
'department', 'roles', 'date_joined'
)
search_fields = ('username', 'email', 'first_name', 'last_name', 'employee_id')
fieldsets = BaseUserAdmin.fieldsets + (
(_('Employee Information'), {
'fields': ('employee_id', 'department', 'manager')
}),
(_('Roles'), {
'fields': ('roles',)
}),
(_('Account Status'), {
'fields': ('is_temporary', 'expires_at')
}),
)
filter_horizontal = ('roles', 'user_permissions')
def get_queryset(self, request):
"""Optimize queryset with select_related"""
return super().get_queryset(request).select_related('manager')
# Migration strategies for custom user models
class CustomUserMigrationStrategy:
"""Strategies for migrating to custom user models"""
@staticmethod
def new_project_setup():
"""Setup custom user model for new projects"""
steps = [
"1. Create custom user model before first migration",
"2. Set AUTH_USER_MODEL in settings.py",
"3. Run makemigrations and migrate",
"4. Create superuser with new model"
]
settings_config = """
# settings.py
AUTH_USER_MODEL = 'myapp.CustomUser'
"""
return {
'steps': steps,
'settings': settings_config,
'difficulty': 'Easy'
}
@staticmethod
def existing_project_migration():
"""Migrate existing project to custom user model"""
# This is complex and often not recommended
# Better to use UserProfile approach
warnings = [
"Migrating existing projects to custom user is very complex",
"Consider using UserProfile model instead",
"Backup database before attempting migration",
"Test thoroughly in development environment"
]
alternative_approach = """
# Instead of migrating, use UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
# Add your custom fields here
# Then access via user.profile.custom_field
"""
return {
'warnings': warnings,
'alternative': alternative_approach,
'difficulty': 'Very Hard'
}
@staticmethod
def data_migration_example():
"""Example data migration for custom user fields"""
migration_code = """
# migrations/0002_migrate_user_data.py
from django.db import migrations
def migrate_user_data(apps, schema_editor):
OldUser = apps.get_model('auth', 'User')
NewUser = apps.get_model('myapp', 'CustomUser')
for old_user in OldUser.objects.all():
NewUser.objects.create(
username=old_user.username,
email=old_user.email,
first_name=old_user.first_name,
last_name=old_user.last_name,
is_staff=old_user.is_staff,
is_active=old_user.is_active,
is_superuser=old_user.is_superuser,
date_joined=old_user.date_joined,
last_login=old_user.last_login,
password=old_user.password,
)
class Migration(migrations.Migration):
dependencies = [
('myapp', '0001_initial'),
]
operations = [
migrations.RunPython(migrate_user_data),
]
"""
return migration_code
Custom user models provide the flexibility to tailor authentication to your application's specific needs. Whether you need email-based authentication, additional required fields, or complex role-based access control, understanding how to implement and manage custom user models enables you to build authentication systems that perfectly fit your requirements while maintaining Django's security best practices.
Login and Logout
Implementing secure and user-friendly login and logout functionality is fundamental to any Django application. Understanding how to handle authentication flows, manage sessions, and provide a smooth user experience while maintaining security is essential for building robust authentication systems.
Middleware for Authentication
Django's middleware system provides powerful hooks for implementing authentication-related functionality that runs on every request. Understanding how to create and configure authentication middleware enables you to implement cross-cutting concerns like session security, user tracking, and custom authentication flows.