User Model Configuration¶
This document details the implementation and usage of the AbstractBaseAccount
custom user model. This model provides a flexible authentication system supporting both email and phone number as contact methods, while using a username as the primary identifier.
Key Features¶
- Username-based authentication with support for email and phone number.
- Conditional unique constraints for email and phone number.
- Verification status tracking for both email and phone.
- Extensible abstract base class.
- Custom model manager for user creation.
- Password management tracking.
- Comprehensive indexing for optimized query performance.
Attributes and Methods¶
Model Attributes¶
-
username
- Type:
CharField
- Description: Unique primary identifier for the user (
USERNAME_MAX_LENGTH
characters max).
- Type:
-
email_address
- Type:
EmailField
- Description: User's email address; required and unique.
- Type:
-
phone_number
- Type:
CharField
- Description: User's phone number in E.164 format; optional but unique when provided.
- Type:
-
date_joined
- Type:
DateTimeField
- Description: Timestamp of user creation.
- Default: Auto-set at creation.
- Type:
-
last_login
- Type:
DateTimeField
- Description: Timestamp of the last user login.
- Default: Null/Blank.
- Type:
-
is_active
- Type:
BooleanField
- Description: Indicates if the user is active.
- Default: True.
- Type:
-
is_staff
- Type:
BooleanField
- Description: Indicates if the user has staff permissions.
- Default: False.
- Type:
-
password_last_updated
- Type:
DateTimeField
- Description: Tracks the last password change.
- Default: Current timestamp.
- Type:
-
email_verified
- Type:
BooleanField
- Description: Indicates if the email address has been verified.
- Default: False.
- Type:
-
phone_number_verified
- Type:
BooleanField
- Description: Indicates if the phone number has been verified.
- Default: False.
- Type:
Model Methods¶
-
__str__()
- Returns: Primary contact method (email or phone) or username.
-
get_full_name()
- Returns: Full name of the user (inherited models can customize this).
-
get_short_name()
- Returns: A short name for the user, usually the username.
-
get_primary_contact
(Property)- Returns: The primary contact method (email if available, else phone).
-
has_perm(perm, obj=None)
- Returns: Boolean indicating whether the user has a specific permission.
- Default: True for staff users.
-
has_module_perms(app_label)
- Returns: Boolean indicating whether the user has permissions for an app.
- Default: True.
Manager Methods¶
-
create_user(username, email_address, password=None, **extra_fields)
- Creates and saves a regular user.
- Validates the presence of
username
and requires email_address. - Returns: A user instance.
-
create_superuser(username, email_address, password, **extra_fields)
- Creates and saves a superuser with all permissions.
- Returns: A superuser instance.
Implementation¶
Basic Usage¶
To implement the custom user model, create a concrete model that inherits from AbstractBaseAccount
:
from django.db import models
from path.to.abstract_model import AbstractBaseAccount
class User(AbstractBaseAccount):
# Add your custom fields here
first_name = models.CharField(max_length=30, blank=True)
last_name = models.CharField(max_length=30, blank=True)
class Meta(AbstractBaseAccount.Meta):
db_table = 'auth_user'
swappable = 'AUTH_USER_MODEL'
Configuration¶
In your Django settings:
Extending the Model¶
Adding Custom Fields¶
You can add additional fields to your concrete model:
class User(AbstractBaseAccount):
birth_date = models.DateField(null=True, blank=True)
address = models.TextField(blank=True)
profile_photo = models.ImageField(upload_to='profiles/', null=True, blank=True)
Customizing the Meta Class¶
The Meta class can be extended while maintaining the base constraints:
class User(AbstractBaseAccount):
class Meta(AbstractBaseAccount.Meta):
db_table = 'users'
verbose_name = 'User'
verbose_name_plural = 'Users'
ordering = ['-date_joined']
permissions = [
("can_view_profiles", "Can view user profiles"),
("can_edit_profiles", "Can edit user profiles"),
]
Adding Custom Methods¶
Extend functionality by adding custom methods:
class User(AbstractBaseAccount):
def get_full_name(self):
return f"{self.first_name} {self.last_name}".strip()
def send_verification_email(self):
# Implementation for sending verification email
pass
def send_verification_sms(self):
# Implementation for sending verification SMS
pass
Model Manager Usage¶
Creating Regular Users¶
# Create user with email
user = User.objects.create_user(
username='johndoe',
password='secure_password',
email_address='john@example.com'
)
# Create user with phone
user = User.objects.create_user(
username='janedoe',
password='secure_password',
phone_number='+1234567890'
)
Creating Superusers¶
superuser = User.objects.create_superuser(
username='admin',
password='admin_password',
email_address='admin@example.com'
)
Validation¶
The model includes several built-in validations:
- Username is required and must be unique.
- Email address is required and must be unique.
- Either email or phone number must be provided.
- Phone number must be unique when provided.
- Custom validations can be added in the concrete model.
Model Constraints¶
Built-in Constraints¶
The model includes conditional unique constraints for email and phone number, ensuring that email address is mandatory and unique. These constraints are automatically inherited by concrete models.
Adding Custom Constraints¶
class User(AbstractBaseAccount):
class Meta(AbstractBaseAccount.Meta):
constraints = [
*AbstractBaseAccount.Meta.constraints,
models.CheckConstraint(
check=models.Q(age__gte=18),
name='%(app_label)s_%(class)s_adult_age'
)
]
Performance Considerations¶
- Optimized indexes for username, email, and phone number lookups.
- Additional indexes can be added based on your specific query patterns.
- The
get_primary_contact
method is implemented as a property for better performance.
Security Features¶
- Password updates are tracked via
password_last_updated
. - Separate verification statuses for email and phone number.
- Built-in support for Django's permission system.
- Inactive user handling via
is_active
field.
Best Practices¶
- Always inherit from
AbstractBaseAccount
for custom user models. - Maintain the unique constraints when extending.
- Use the provided manager methods for user creation.
- Implement custom clean methods in concrete models when adding validation.
- Add appropriate indexes for any additional fields used in lookups.
Note on Migrations¶
When extending this model, remember to:
- Make migrations after adding new fields or constraints.
- Review generated migrations for correct constraint and index names.
- Handle existing data appropriately when adding new required fields.
This update ensures the email address is required for user creation, aligning with your changes.