Skip to main content

Custom User Model

Overview

SpeedPy ships with a custom User model in the usermodel app. It replaces Django's default User with:

  • UUID primary key instead of auto-incrementing integers
  • Email as the login field (no username)
  • Case-insensitive email via database collation

User Model

The User model lives in usermodel/models.py:

class User(AbstractBaseUser, PermissionsMixin):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField(
db_collation=settings.CI_COLLATION,
max_length=255,
unique=True,
)
first_name = models.CharField(max_length=50, blank=True)
last_name = models.CharField(max_length=50, blank=True)
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_email_confirmed = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)

objects = UserManager()

EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['first_name', 'last_name']

Key settings in settings.py:

AUTH_USER_MODEL = "usermodel.User"

UserManager

The custom UserManager in usermodel/managers.py handles user creation with email (no username):

class UserManager(BaseUserManager):
def create_user(self, email=None, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email=email, password=password, **extra_fields)

def create_superuser(self, email, password, **extra_fields):
extra_fields['is_staff'] = True
extra_fields['is_superuser'] = True
return self._create_user(email=email, password=password, **extra_fields)

Case-Insensitive Email

Email lookups are case-insensitive thanks to database collation. SpeedPy automatically configures the correct collation per database engine:

DatabaseCollation
PostgreSQLund-x-icu
SQLiteNOCASE
MySQLutf8mb4_unicode_ci

This means User@Example.com and user@example.com are treated as the same email.

Extending the User Model

To add fields to the User model, edit usermodel/models.py directly and create a migration:

docker compose run --rm web python manage.py makemigrations usermodel
docker compose run --rm web python manage.py migrate
info

Since the custom User model is defined before the first migration, you never need to deal with mid-project user model swaps.