Introduction
Custom user managers handle the creation of user instances. They're essential when using AbstractBaseUser.
Key Concepts
BaseUserManager: Base class for user managers.
normalize_email(): Lowercases the domain part of email.
Real World Context
In a multi-tenant platform where users sign up with their work email, the custom user manager is where you enforce business rules like requiring a company domain, normalizing emails, and auto-assigning the user to the correct organization based on their email domain.
Deep Dive
Complete Manager Implementation
pythonfrom django.contrib.auth.models import BaseUserManager class CustomUserManager(BaseUserManager): def create_user(self, email, password=None, **extra_fields): """Create and return a regular user.""" if not email: raise ValueError('Email address is required') email = self.normalize_email(email) extra_fields.setdefault('is_active', True) user = self.model(email=email, **extra_fields) user.set_password(password) # Hashes the password user.save(using=self._db) return user def create_superuser(self, email, password=None, **extra_fields): """Create and return a superuser.""" extra_fields.setdefault('is_staff', True) extra_fields.setdefault('is_superuser', 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) def get_by_natural_key(self, email): """Case-insensitive email lookup.""" return self.get(email__iexact=email)
Using the Manager
pythonclass User(AbstractBaseUser, PermissionsMixin): email = models.EmailField(unique=True) name = models.CharField(max_length=255) is_active = models.BooleanField(default=True) is_staff = models.BooleanField(default=False) objects = CustomUserManager() # Attach manager USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['name'] # For createsuperuser
Common Pitfalls
- Storing raw passwords instead of using
set_password(): If you assignuser.password = raw_password, the password is stored in plain text. Always useset_password(), which applies the configured hasher. - Forgetting
normalize_email(): Email addresses are case-sensitive in the local part (before @) per RFC 5321, but Django'snormalize_email()lowercases the domain part to prevent duplicate accounts. - Not overriding
create_superuser(): Thecreatesuperusermanagement command callscreate_superuser(). If it falls back tocreate_user()without settingis_staff=Trueandis_superuser=True, your admin account will not have admin access.
Best Practices
- Always use set_password(): Never store plain text passwords.
- Use normalize_email(): Ensures consistent email format.
- Validate in manager: Check required fields.
Summary
Custom managers handle user creation. Use set_password() for security. Include validation for required fields.