Django’s default User model is fine for many projects, but you’ll eventually hit a wall where you need to store more information about your users than just username, password, email, and basic permissions. This could be anything from a user’s profile picture, a company they belong to, or even custom fields for authentication. The standard way to do this is by extending Django’s AbstractUser or AbstractBaseUser.
Here’s a UserProfile model that adds a bio and a profile_picture to the default user:
# users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class UserProfile(AbstractUser):
bio = models.TextField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='profile_pics/', blank=True, null=True)
def __str__(self):
return self.username
To make Django use this new model, you need to tell it in your settings.py:
# settings.py
AUTH_USER_MODEL = 'users.UserProfile'
The users part here corresponds to the name of the Django app where you’ve defined your UserProfile model. If you put it in an app called accounts, it would be AUTH_USER_MODEL = 'accounts.UserProfile'.
Now, before you run python manage.py migrate for the first time (or if you haven’t created any migrations for the auth app yet), Django knows to use your custom model for all authentication-related operations. If you have already created migrations for the auth app, you’ll need to delete them and re-run makemigrations and migrate.
Let’s see this in action. Imagine you have a simple Django project set up with an app named users.
Project Structure:
myproject/
├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── users/
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── manage.py
users/models.py:
from django.contrib.auth.models import AbstractUser
from django.db import models
class UserProfile(AbstractUser):
bio = models.TextField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='profile_pics/', blank=True, null=True)
def __str__(self):
return self.username
myproject/settings.py:
# ... other settings ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users', # Add your app here
]
MIDDLEWARE = [
# ...
]
ROOT_URLCONF = 'myproject.urls'
TEMPLATES = [
# ...
]
WSGI_APPLICATION = 'myproject.wsgi.application'
# ... other settings ...
# THIS IS THE KEY SETTING
AUTH_USER_MODEL = 'users.UserProfile'
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
After setting this up, you’d typically run:
python manage.py makemigrations users
This will generate a migration file for your UserProfile model. If you’ve already run makemigrations for auth (or if this is a new project where auth migrations have already been created by default), you’ll need to remove those initial auth migrations and then run makemigrations again.
Then, apply the migrations:
python manage.py migrate
Now, when you create a new user through the Django admin or programmatically, they will have the bio and profile_picture fields.
Let’s simulate creating a user in the Django shell:
>>> from users.models import UserProfile
>>> user = UserProfile.objects.create_user(username='alice', email='alice@example.com', password='password123')
>>> user.bio = "A curious developer exploring Django."
>>> user.save()
>>> print(user.username)
alice
>>> print(user.bio)
A curious developer exploring Django.
The power of AUTH_USER_MODEL is that it’s a global setting. Once you define it before creating your initial migrations, Django’s entire authentication system, including User.objects, login, logout, and permission checks, will seamlessly use your custom model. You don’t need to manually replace django.contrib.auth.get_user_model() calls in your code; Django does it for you under the hood.
The most surprising thing about this setup is how little your code changes. You can often drop in a custom user model and, as long as you’ve set AUTH_USER_MODEL early and re-run migrations, the rest of your authentication logic remains the same. You’re just adding fields to the core user object.
One aspect that often trips people up is how to handle existing users if you decide to switch to a custom user model after your project has been running and has users. This is a more advanced topic involving data migration scripts to copy existing user data into the new fields. However, if you’re starting a new project, setting this up from the get-go is straightforward.
The next step in user management is often integrating social authentication or implementing more complex profile editing flows.