Skip to 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:

Database Collation
PostgreSQL und-x-icu
SQLite NOCASE
MySQL utf8mb4_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.