Chapter 8: Custom User Model
Django's built-inUser modelallows us to start working with users right away, as we just did with our Blog app in the previous chapters. However theofficial Djangodocumentationhighly recommends using a custom user model for new projects. Thereason is that if you want to make any changes to the User model down the road–-for example adding an age field-–using a custom user model from the beginning makes this quite easy. But if you do not create a custom user model, updating the default User model in an existing Django project is very, very challenging.
So always use a custom user model for all new Django projects. But the approach demonstrated in the official documentationexampleis actually not what many Django experts recommend. It uses the quite complex AbstractBaseUser when if we just use AbstractUser instead things are far simpler and still customizable.
Thus we will use AbstractUser in this chapter where we start a new Newspaper app properly with a custom user model. The choice of a newspaper app pays homage to Django's roots as a web framework built for editors and journalists at the Lawrence Journal-World.
Set Up
The first step is to create a new Django project from the command line. We need to do several things:
create and navigate into a new directory for our code
create a new virtual environment news
Chapter 8: Custom User Model | 168 |
---|
install Django
make a new Django project newspaper_project
make a new app users
We're calling our app for managing users users here but you'll also see it frequently called accounts in open source code. The actual name doesn't matter as long as you are consistent when referring to it throughout the project.
Here are the commands to run:
Command Line
cd ~/Desktop
mkdir news
cd news
pipenv install django==3.0.1
pipenv shell
(news) $ django-admin startproject newspaper_project .
(news) $ python manage.py startapp users
(news) $ python manage.py runserver
Note that we did not run migrate to configure our database. It's important to wait until after we've created our new custom user model before doing so given how tightlyconnected the user model is to the rest of Django.
If you navigate tohttp://127.0.0.1:8000you'll see the familiar Django welcome screen.
Chapter 8: Custom User Model | 169 |
---|
Welcome page
Custom User Model
Creating our custom user model requires four steps:
update settings.py
create a new CustomUser model
create new forms for UserCreationForm and UserChangeForm
update users/admin.py
In settings.py we'll add the users app to our INSTALLED_APPS. Then at the bottom of the file use the AUTH_USER_MODEL config to tell Django to use our new custom user model
Chapter 8: Custom User Model | 170 |
---|
in place of the built-in User model. We'll call our custom user model CustomUser so, since it exists within our users app we refer to it as users.CustomUser.
Code
- newspaper_project/settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', # new
]
...
AUTH_USER_MODEL = 'users.CustomUser' # new
Now update users/models.py with a new User model which we'll call CustomUser that extends the existing AbstractUser. We also include our first custom field, age, here.
Code
users/models.py
from django.contrib.auth.models import AbstractUser from django.db import models
class CustomUser (AbstractUser):
age = models.PositiveIntegerField(null=True, blank=True)
If you read theofficial documentation on custom user modelsit recommends using AbstractBaseUser not AbstractUser. This needlessly complicates things in my opinion,especially for beginners.
Chapter 8: Custom User Model | 171 |
---|
AbstractBaseUser vs AbstractUser
AbstractBaseUser requires a very fine level of control and customization. We essen-tially rewrite Django. This can be helpful, but if we just want a custom user model that can be updated with additional fields, the better choice is AbstractUser which subclasses AbstractBaseUser. In other words, we write much less code and have less opportunity to mess things up. It's the better choice unless you really know what you're doing with Django!
Note that we use bothnullandblankwith our age field. These two terms are easy to confuse but quite distinct:
- null is database-related. When a field has null=True it can store a database entry as NULL, meaning no value.
- blank is validation-related , if blank=True then a form will allow an empty value, whereas if blank=False then a value is required.
In practice, null and blank are commonly used together in this fashion so that a form allows an empty value and the database stores that value as NULL.
A common gotcha to be aware of is that the field type dictates how to use these values. Whenever you have a string-based field like CharField or TextField, setting both null and blank as we've done will result in two possible values for "no data" in the database. Which is a bad idea. The Django convention is instead to use the empty string '', not NULL.
Forms
If we step back for a moment, what are the two ways in which we would interact with our new CustomUser model? One case is when a user signs up for a new account on our
Chapter 8: Custom User Model | 172 |
---|
website. The other is within the admin app which allows us, as superusers, to modify existing users. So we'll need to update the two built-in forms for this functionality: UserCreationFormandUserChangeForm.
Stop the local server with Control+c and create a new file in the users app called forms.py.
Command Line
(news) $ touch users/forms.py
We'll update it with the following code to extend the existing UserCreationForm and UserChangeForm forms.
Code
users/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm (UserCreationForm):
class Meta (UserCreationForm):
model = CustomUser
fields = UserCreationForm.Meta.fields + ('age',)
class CustomUserChangeForm (UserChangeForm):
Chapter 8: Custom User Model | 173 |
---|
class Meta :
model = CustomUser
fields = UserChangeForm.Meta.fields
For both new forms we are using theMeta classto override the default fields by setting the model to our CustomUser and using the default fields via Meta.fields which includes all default fields. To add our custom age field we simply tack it on at the end and itwill display automatically on our future sign up page. Pretty slick, no?
The concept of fields on a form can be confusing at first so let's take a moment to explore it further. Our CustomUser model contains all the fields of the default User model and our additional age field which we set.
But what are these default fields? It turns out thereare manyincluding username, first_name, last_name, email, password, groups, and more. Yet when a user signs upfor a new account on Django the default form only asks for a username, email, and password. This tells us that the default setting for fields on UserCreationForm is just username, email, and password even though there are many more fields available.
This might not click for you since understanding forms and models properly takes some time. In the next chapter we will create our own sign up, log in, and log out pages which will tie together our CustomUser model and forms more clearly. So hang tight!
The only other step we need is to update our admin.py file since Admin is tightly coupled to the default User model. We will extend the existingUserAdminclass to use our new CustomUser model.
Chapter 8: Custom User Model | 174 |
---|
Code
users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm from .models import CustomUser
class CustomUserAdmin (UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
admin.site.register(CustomUser, CustomUserAdmin)
Ok we're done! Type Control+c to stop the local server and go ahead and run makemigrations and migrate for the first time to create a new database that uses thecustom user model.
Command Line
(news) $ python manage.py makemigrations users
(news) $ python manage.py migrate
Superuser
Let's create a superuser account to confirm that everything is working as expected.
Chapter 8: Custom User Model | 175 |
---|
On the command line type the following command and go through the prompts.
Command Line
(news) $ python manage.py createsuperuser
The fact that this works is the first proof our custom user model works as expected.
Let's view things in the admin too to be extra sure.
Start up the web server.
Command Line
(news) $ python manage.py runserver
Then navigate to the admin athttp://127.0.0.1:8000/adminand log in.
Admin page
If you click on the link for "Users" you should see your superuser account as well as the default fields of Username, Email Address, First Name, Last Name, and Staff Status.
Chapter 8: Custom User Model | 176 |
---|
Admin one user
We can control the fields listed here via the list_display setting for CustomUserAdmin. Let's do that now so that it displays email, username, age, and staff status. This is a one-line change.
Code
users/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserCreationForm, CustomUserChangeForm from .models import CustomUser
class CustomUserAdmin (UserAdmin):
Chapter 8: Custom User Model | 177 |
---|
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['email', 'username', 'age', 'is_staff',] # new
admin.site.register(CustomUser, CustomUserAdmin)
Refresh the page and you should see the update.
Admin custom list display
Chapter 8: Custom User Model | 178 |
---|
Conclusion
With our custom user model complete, we can now focus on building out the rest of our Newspaper app. In the next chapter we will configure and customize sign up, log in, and log out pages.
'DoItDJango' 카테고리의 다른 글
User Authentication (0) | 2023.04.17 |
---|---|
Django db.sqlite3 초기화 (0) | 2023.04.16 |
Forms (0) | 2023.04.15 |
Blog App (0) | 2023.04.15 |
텍스트 콘텐츠를 저장하는 모델 만들기 (0) | 2023.04.14 |
댓글